Sobes.tech
Middle

Por que e como sobrescrever o método hashCode?

sobes.tech IA

Resposta da IA

O método hashCode é usado para obter o código de hash inteiro de um objeto. É necessário para o funcionamento correto de coleções baseadas em tabelas de hash, como HashMap, HashSet e HashTable. Quando um objeto é adicionado a tal coleção, seu código de hash é usado para determinar a "cesta" (bucket) onde o objeto será colocado. Ao procurar um objeto, o código de hash é recalculado para identificar rapidamente a cesta a ser examinada.

É necessário sobrescrever hashCode se você sobrescrever o método equals. De acordo com o contrato entre equals e hashCode:

  1. Se dois objetos são iguais segundo equals, seus códigos de hash, retornados por hashCode, devem ser iguais.
  2. Se dois objetos não são iguais segundo equals, seus códigos de hash não precisam ser diferentes, mas para melhor desempenho das coleções, seria preferível que fossem.

Se você sobrescrever equals sem sobrescrever hashCode, objetos iguais terão códigos de hash diferentes (exceto coincidências aleatórias), o que levará a um funcionamento incorreto das coleções:

  • Objetos considerados iguais por equals serão colocados em cestas diferentes.
  • A busca por um objeto igual pode falhar, pois apenas uma cesta determinada pelo código de hash do objeto buscado será verificada.

Para sobrescrever hashCode, geralmente combina-se o código de hash dos campos do objeto que participam na comparação de igualdade em equals.

As formas mais comuns de sobrescrever:

  1. Uso de Objects.hash() (desde Java 7): A forma mais simples e recomendada. É segura para null e usa os códigos de hash dos campos fornecidos.

    // Exemplo de uso de Objects.hash()
    import java.util.Objects;
    
    public class MyClass {
        private int field1;
        private String field2;
    
        // ... construtor, getters, setters
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            MyClass myClass = (MyClass) o;
            return field1 == myClass.field1 &&
                   Objects.equals(field2, myClass.field2);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(field1, field2); // Passa os campos que participam em equals
        }
    }
    
  2. Manual (usando um algoritmo simples): Abordagem de baixo nível, que pode ser útil para compreensão, mas mais propensa a erros. Normalmente usa multiplicação e soma simples.

    // Exemplo de sobrescrita manual de hashCode
    public class MyClass {
        private int field1;
        private String field2;
    
        // ... construtor, getters, setters
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            MyClass myClass = (MyClass) o;
            return field1 == myClass.field1 &&
                   Objects.equals(field2, myClass.field2);
        }
    
        @Override
        public int hashCode() {
            int result = 17; // Valor inicial (qualquer número ímpar)
            result = 31 * result + field1; // Multiplicamos por um número primo ímpar (31 é padrão) e adicionamos o hash do campo
            result = 31 * result + (field2 != null ? field2.hashCode() : 0); // Tratamento de null para objetos
    
            return result;
        }
    }
    

O número primo ímpar padrão (31) é usado porque a multiplicação por 31 pode ser otimizada pelo compilador (31 * i == (i << 5) - i).

  1. Uso de IDE: A maioria das IDEs modernas (IntelliJ IDEA, Eclipse) possui funções para gerar automaticamente os métodos equals() e hashCode(). Essa é a abordagem mais segura e recomendada, pois elas geram código de acordo com as melhores práticas.

O principal objetivo de sobrescrever hashCode é garantir que objetos iguais tenham códigos de hash iguais, o que é fundamental para o funcionamento correto de coleções baseadas em hash.