Check if ArrayList has equal items

1

I have class Pessoa :

String Nome, Cidade;

public Pessoa(String Nome, String Cidade){
    this.Nome = Nome;
    this.Cidade = Cidade;
}


public String getNome() {
    return Nome;
}

public String getCidade() {
    return Cidade;
}

I have the class that adds the Pessoas to a ArrayList :

ArrayList<Pessoas> pessoas = new ArrayList<>();

public void cadastrarPessoas(){
    Pessoa pessoa = new Pessoa(Nome, Cidade);
    pessoas.add(pessoa);
}

How can I make return true if in the list of people contains the same added element (example below): People person = new Person

Leandro|SP
Leandro|SP

As I wanted:

Leandro|SP
Leandro|RC

I tried this way but nothing happens:

Pessoa pessoa2 = new Pessoa(Nome, Cidade);

    for (Pessoa pessoa : pessoas){
                if (pessoa.equals(pessoa2)){
                    iguais
                }else{
                    pessoas.add(pessoa2);
                    diferentes
                }
            }
    
asked by anonymous 30.08.2018 / 00:50

1 answer

6

When you create the class Pessoa :

public class Pessoa {
    private String nome;
    private String cidade;

    public Pessoa(String nome, String cidade) {
        this.nome = nome;
        this.cidade = cidade;
    }

    // ... getters e setters
}

How do I know if 2 instances of this class are the same? The first idea is to use the == operator, since it works for numbers, so why would not it work with objects?

// primeira tentativa de comparar 2 pessoas
Pessoa p1 = new Pessoa("Fulano", "SP");
Pessoa p2 = new Pessoa("Fulano", "SP");
System.out.println(p1 == p2); // false

But this code prints false . This is because the == operator verifies that p1 and p2 are the same object. Since each was created with new , they are 2 different objects (albeit with the same values).

Ah, but wait, there is the equals method. Let's try to use it?

// segunda tentativa de comparar 2 pessoas
Pessoa p1 = new Pessoa("Fulano", "SP");
Pessoa p2 = new Pessoa("Fulano", "SP");
System.out.println(p1.equals(p2)); // false

This code also prints false . This is because the equals method was inherited from Object , "and internally this method uses the == operator. I traded 6 for half a dozen.

For me to consider 2 instances of Pessoa equal based on their values , I should override the equals method. That is, the class Pessoa must have its own version of equals , with the rule that defines what makes 2 people equal.

In this case, 2 people are equal if their names and cities are the same, so the method would look like this:

public class Pessoa {
    ....

    @Override
    public boolean equals(Object obj) {
        Pessoa other = (Pessoa) obj;
        // verifica se o nome e cidade são iguais
        return this.nome.equals(other.nome) && this.cidade.equals(other.cidade);
    }
}

Of course this implementation can be improved by testing whether the fields are null , if obj is even an instance of Pessoa , etc, but we will keep it simple for didactic purposes.

It is also important to override the hashCode method. In this link there is a good answer detailing the reasons and how to implement it properly. Here I leave the implementation suggested by Eclipse, but I suggest you read the link provided to better understand this method:

public class Pessoa {
    ....
    // equals, getters, setters, construtor, etc

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((cidade == null) ? 0 : cidade.hashCode());
        result = prime * result + ((nome == null) ? 0 : nome.hashCode());
        return result;
    }
}

With this, now the class Pessoa knows to check if another Pessoa is equal to it:

// terceira tentativa de comparar 2 pessoas
Pessoa p1 = new Pessoa("Fulano", "SP");
Pessoa p2 = new Pessoa("Fulano", "SP");
System.out.println(p1.equals(p2)); // true

Now this code prints true .

So if you have a list of Pessoa , you can easily check if any Pessoa already belongs to the list:

// cria a lista e insere pessoas nela
List<Pessoa> listaPessoas = new ArrayList<>();
listaPessoas.add(new Pessoa("Fulano", "SP"));
listaPessoas.add(new Pessoa("Fulano", "RC"));

// verifica se uma pessoa qualquer pertence à lista
Pessoa pessoa = new Pessoa("Fulano", "SP");
System.out.println(listaPessoas.contains(pessoa));

The contains method uses the equals method of Pessoa to know if 2 people are equal, and thereby checks that pessoa already belongs to the list. This code above prints true ("Fulano / SP" belongs to the list).

Of course you could also do for in the list and go comparing each person with the equals method, but what to do if contains does exactly the same thing?

Furthermore, you are making for in the list and adding an element in the same list within for , which will generate java.util.ConcurrentModificationException . It's best to use contains same:

Pessoa pessoa = new Pessoa("Fulano", "SP");
// se a pessoa não pertence à lista, adiciona
if (! listaPessoas.contains(pessoa)) {
    listaPessoas.add(pessoa);
}

But if you want a collection of several people without having repeated it, it is better to use a java.util.Set , since a set does not allow elements equal:

// usar Set ao invés de List
Set<Pessoa> setPessoas = new HashSet<>();
// inserir pessoas
setPessoas.add(new Pessoa("Fulano", "SP"));
setPessoas.add(new Pessoa("Fulano", "RC"));
// tentar inserir Fulano/SP de novo
setPessoas.add(new Pessoa("Fulano", "SP"));

// imprime o tamanho do set
System.out.println(setPessoas.size());

This code prints 2 , because Set will only have 2 elements. The second attempt to insert "Fulano / SP" does not work since it already belongs to Set .

If I used List , I would have to check if the element already belongs to the list before adding it (which also works, but using Set deletes this step).

If anyway you need a List , you can create it from Set :

// cria um List com os elementos do Set
List<Pessoa> listaPessoas = new ArrayList<>(setPessoas);

Another way to implement hashCode is by using java.util.Objects (in the plural, not to be confused with java.lang.Object (in the singular)):

@Override
public int hashCode() {
    // calcula o hashCode usando nome e cidade
    return Objects.hash(cidade, nome);
}
    
30.08.2018 / 03:16