Java equals()
e hashcode()
são dois métodos que funcionam juntos para verificar se dois objetos têm o mesmo valor. Você pode usá-los para tornar as comparações de objetos fáceis e eficientes em seus programas Java.
Java é igual() e hashcode()
Neste artigo, você aprenderá:
- Por que substituir
equals()
ehashcode()
em Java? - Como comparar objetos Java com
equals()
- Como identificar objetos Java com
hashcode()
- Como usar
equals()
ehashcode()
com coleções
Você também receberá:
- Diretrizes para uso
equals()
ehashcode()
- Regras para fazer comparações de objetos com
equals()
ehashcode()
- Erros a evitar ao usar
equals()
ehashcode()
- O que lembrar
equals()
ehashcode()
Por que substituir equals() e hashcode() em Java?
Substituição de método é uma técnica onde o comportamento da classe pai ou interface é escrito novamente (substituído) na subclasse para aproveitar as vantagens do polimorfismo. Todo Object
em Java inclui um equals()
e um hashcode()
método, mas eles devem ser substituídos para funcionar corretamente.
Para entender como a substituição funciona com equals()
e hashcode()
, podemos estudar sua implementação nas principais classes Java. Abaixo está o equals()
método no Object
aula. O método está verificando se a instância atual é igual à passada anteriormente Object
.
public boolean equals(Object obj) {
return (this == obj);
}
Quando o hashcode()
método não é substituído, o método padrão no Object
classe será invocada. Isto é um método nativo, o que significa que será executado em outra linguagem como C, e retornará algum código referente ao endereço de memória do objeto. (Não é tão importante saber exatamente como esse método funciona, a menos que você esteja escrevendo código JDK.)
@HotSpotIntrinsicCandidate
public native int hashCode();
Quando o equals()
e hashcode()
métodos não são substituídos, você verá os métodos acima invocados. Neste caso, os métodos não cumprem o verdadeiro propósito de equals()
e hashcode()
que serve para verificar se dois ou mais objetos têm os mesmos valores.
Como regra, quando você substitui equals()
você também deve substituir hashcode()
.
Como comparar objetos com equals()
Nós usamos o equals()
método para comparar objetos em Java. Para determinar se dois objetos são iguais, equals()
compara os valores dos atributos dos objetos:
public class EqualsAndHashCodeExample {
public static void main(String... equalsExplanation) {
System.out.println(new Simpson("Homer", 35, 120)
.equals(new Simpson("Homer",35,120)));
System.out.println(new Simpson("Bart", 10, 120)
.equals(new Simpson("El Barto", 10, 45)));
System.out.println(new Simpson("Lisa", 54, 60)
.equals(new Object()));
}
static class Simpson {
private String name;
private int age;
private int weight;
public Simpson(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Simpson simpson = (Simpson) o;
return age == simpson.age &&
weight == simpson.weight &&
name.equals(simpson.name);
}
}
}
Na primeira comparação, equals()
compara a instância do objeto atual com o objeto que foi passado. Se os dois objetos tiverem os mesmos valores, equals()
retorna true
.
Na segunda comparação, equals()
verifica se o objeto passado é nulo, ou se for digitado como uma classe diferente. Se for uma classe diferente, os objetos não serão iguais.
Finalmente, equals()
compara os campos dos objetos. Se dois objetos tiverem os mesmos valores de campo, então os objetos serão iguais.
Comparações de objetos
Agora, vamos ver os resultados dessas comparações em nosso main()
método. Primeiro, comparamos dois Simpson
objetos:
System.out.println(new Simpson("Homer", 35, 120).equals(new Simpson("Homer", 35, 120)));
Os objetos aqui são idênticos, então o resultado será true
.
A seguir, comparamos dois Simpson
objetos novamente:
System.out.println(new Simpson
("Bart", 10, 45).equals(new Simpson
("El Barto", 10, 45)));
Os objetos aqui são quase idênticos, mas seus nomes são diferentes: Bart e El Barto. Portanto o resultado será false
.
Finalmente, vamos comparar um Simpson
object e uma instância da classe Object:
System.out.println(new Simpson
("Lisa", 54, 60).equals(new Object
()));
Neste caso o resultado será false
porque os tipos de classe são diferentes.
equals() não é o mesmo que ==
À primeira vista, o ==
operador e equals()
método pode parecer fazer a mesma coisa, mas funciona de maneira diferente. O ==
operador compara se duas referências de objeto apontam para o mesmo objeto. Por exemplo:
System.out.println(homer == homer2);
Na primeira comparação, instanciamos dois diferentes Simpson
instâncias usando o new
operador. Por causa disso, as variáveis homer
e homer2
apontará para diferentes Object
referências no heap de memória. Então teremos false
como resultado.
System.out.println(homer.equals(homer2));
Na segunda comparação, substituímos o equals()
método. Neste caso apenas os nomes são comparados. Porque o nome de ambos Simpson
objetos é “Homer”, o resultado é true
.
Como identificar objetos com hashcode()
Nós usamos o hashcode()
método para otimizar o desempenho ao comparar objetos. Executando hashcode()
retorna um ID exclusivo para cada objeto em seu programa, o que torna a tarefa de comparar todo o estado do objeto muito mais fácil.
Se o código hash de um objeto não for igual ao código hash de outro objeto, não há razão para executar o código hash. equals()
método: você apenas sabe que os dois objetos não são iguais. Por outro lado, se o código hash é o mesmo, então você deve executar o equals()
método para determinar se os valores e campos são iguais.
Aqui está um exemplo prático com hashcode()
.
public class HashcodeConcept {
public static void main(String... hashcodeExample) {
Simpson homer = new Simpson(1, "Homer");
Simpson bart = new Simpson(2, "Homer");
boolean isHashcodeEquals = homer.hashCode() == bart.hashCode();
if (isHashcodeEquals) {
System.out.println("Should compare with equals method too.");
} else {
System.out.println("Should not compare with equals method because " +
"the id is different, that means the objects are not equals for sure.");
}
}
static class Simpson {
int id;
String name;
public Simpson(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Simpson simpson = (Simpson) o;
return id == simpson.id &&
name.equals(simpson.name);
}
@Override
public int hashCode() {
return id;
}
}
}
A hashcode()
que sempre retorna o mesmo valor é válido, mas não muito eficaz. Neste caso a comparação sempre retornará true
então o equals()
método sempre será executado. Não há melhoria de desempenho neste caso.
Como usar equals() e hashcode() com coleções
O Set
interface é responsável por garantir que nenhum elemento duplicado seja inserido em um Set
subclasse. A seguir estão algumas das classes que implementam o Set
interface:
Somente elementos únicos podem ser inseridos em um Set
então se você quiser adicionar um elemento ao HashSet
classe (por exemplo), você deve primeiro usar o equals()
e hashcode()
métodos para verificar se o elemento é único. Se o equals()
e hashcode()
métodos não foram substituídos neste caso, você correria o risco de inserir elementos duplicados no código.
No código abaixo, estamos usando o add
método para adicionar um novo elemento a um HashSet
objeto. Antes do novo elemento ser adicionado, HashSet
verifica se o elemento já existe na coleção fornecida:
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
Se o objeto for o mesmo, o novo elemento não será inserido.
Diretrizes para usar equals() e hashcode()
Você só deve executar um equals()
método para objetos que possuem o mesmo ID de código hash exclusivo. Você deve não executar equals()
quando o ID do hashcode é diferente.
Este princípio é usado principalmente em Set
ou Hash
coleções por motivos de desempenho.
Regras para comparação de objetos com equals() e hashcode()
Quando um hashcode()
retornos de comparação false
o equals()
método também deve retornar falso. Se o código hash for diferente, os objetos definitivamente não são iguais.
Quando o equals()
método retorna true
isso significa que os objetos são iguais em todos os valores e atributos. Nesse caso, a comparação do código hash também deve ser verdadeira.
Aceite o desafio equals() e hashcode()!
É hora de testar suas habilidades com equals()
e hashcode()
. Seu objetivo neste desafio é descobrir o resultado dos dois equals()
comparações de métodos e adivinhar o tamanho do Set
coleção.
Para começar, estude cuidadosamente o seguinte código:
public class EqualsHashCodeChallenge {
public static void main(String... doYourBest) {
System.out.println(new Simpson("Bart").equals(new Simpson("Bart")));
Simpson overriddenHomer = new Simpson("Homer") {
public int hashCode() {
return (43 + 777) + 1;
}
};
System.out.println(new Simpson("Homer").equals(overriddenHomer));
Set set = new HashSet(Set.of(new Simpson("Homer"), new Simpson("Marge")));
set.add(new Simpson("Homer"));
set.add(overriddenHomer);
System.out.println(set.size());
}
static class Simpson {
String name;
Simpson(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
Simpson otherSimpson = (Simpson) obj;
return this.name.equals(otherSimpson.name) &&
this.hashCode() == otherSimpson.hashCode();
}
@Override
public int hashCode() {
return (43 + 777);
}
}
}
Lembre-se, analise o código primeiro, adivinhe o resultado, então execute o código. Seu objetivo é melhorar sua habilidade com análise de código e absorver os principais conceitos Java para tornar seu código mais poderoso. Escolha sua resposta antes de marcar a correta abaixo.
A)
true
true
4
B)
true
false
3
C)
true
false
2
D)
false
true
3
O que acabou de acontecer?
Em primeiro equals()
comparação de métodos, o resultado é true
porque o estado do objeto é exatamente o mesmo e o hashcode()
O método retorna o mesmo valor para ambos os objetos.
No segundo equals()
comparação de métodos, o hashcode()
método está sendo substituído pelo overridenHomer
variável. O nome é “Homer” para ambos Simpson
objetos, mas o hashcode()
método retorna um valor diferente para overriddenHomer
. Neste caso, o resultado final do equals()
método será false
porque o método contém uma comparação com o hashcode.
Você pode notar que o tamanho da coleção está definido para conter três Simpson
objetos. Vamos verificar isso de forma detalhada.
O primeiro objeto do conjunto será inserido normalmente:
new Simpson("Homer");
O próximo objeto também será inserido normalmente, pois contém um valor diferente do objeto anterior:
new Simpson("Marge");
Finalmente, o seguinte Simpson
objeto tem o mesmo valor que o primeiro objeto. Neste caso o objeto não será inserido:
set.add(new Simpson("Homer"));
Como sabemos, o overridenHomer
objeto usa um valor de hashcode diferente do normal Simpson(“Homer”)
instanciação. Por este motivo, este elemento será inserido na coleção:
overriddenHomer;
Palavra chave
A resposta para este desafiante Java é B. A saída seria:
true
false
3