Fale sobre o contrato dos métodos hashCode e equals em Java.
sobes.tech IA
Resposta da IA
O contrato dos métodos hashCode e equals em Java define a sua relação e garante um comportamento correto dos objetos ao usar coleções baseadas em tabelas de dispersão (por exemplo, HashSet, HashMap).
Regras principais do contrato:
- Reflexividade: Para qualquer valor de referência não nulo
x,x.equals(x)deve retornartrue. - Simetria: Para quaisquer valores de referência não nulos
xey,x.equals(y)deve retornartruese e somente sey.equals(x)retornartrue. - Transitividade: Para quaisquer valores de referência não nulos
x,yez, sex.equals(y)retornatrueey.equals(z)retornatrue, entãox.equals(z)deve retornartrue. - Consistência: Para quaisquer valores de referência não nulos
xey, múltiplas chamadas ax.equals(y)com os mesmos objetosxeydevem sempre retornar o mesmo valor. - Não nulidade: Para qualquer valor de referência não nulo
x,x.equals(null)deve retornarfalse.
E a regra principal que liga hashCode e equals:
- Se dois objetos são iguais de acordo com o método
equals(Object), a chamada ao métodohashCode()para cada um desses objetos deve produzir o mesmo inteiro. - O inverso não é verdadeiro: se dois objetos têm o mesmo código hash, isso não significa que são iguais.
Valor do contrato:
O não cumprimento do contrato pode levar ao funcionamento incorreto das coleções. Por exemplo, se dois objetos iguais retornam códigos hash diferentes, o HashMap pode colocá-los em diferentes "cestos" (buckets) e não conseguir encontrar um objeto por outro, mesmo que sejam logicamente iguais.
Exemplo de implementação correta:
// Exemplo de classe com equals e hashCode corretos
public final class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
return x == point.x && y == point.y;
}
@Override
public int hashCode() {
int result = 17; // Número mágico, geralmente primo
result = 31 * result + x;
result = 31 * result + y;
return result;
}
// Getters (não obrigatórios para equals/hashCode)
public int getX() {
return x;
}
public int getY() {
return y;
}
}
Uso:
- Sempre sobrescreva ambos os métodos (
equalsehashCode) juntos. - Ao calcular
hashCode, use os mesmos campos utilizados emequals. - Tente criar "bons" funções de hash que distribuam uniformemente os códigos de hash para minimizar colisões e melhorar o desempenho das coleções hash.
- Para tipos primitivos, use seus valores. Para objetos, chame
hashCode(). Para arrays, use métodos deArrays.
Na Java 7+ recomenda-se usar Objects.equals(Object a, Object b) e Objects.hash(Object... values) para maior conveniência e correção.
Exemplo com Objects:
// Classe com equals e hashCode usando Objects
import java.util.Objects;
public final class PointWithObjects {
private final int x;
private final int y;
public PointWithObjects(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PointWithObjects that = (PointWithObjects) o;
return x == that.x && y == that.y; // A lógica de comparação permanece
}
@Override
public int hashCode() {
return Objects.hash(x, y); // Método conveniente para calcular hash
}
}