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);
}