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() e hashcode() em Java?
  • Como comparar objetos Java com equals()
  • Como identificar objetos Java com hashcode()
  • Como usar equals() e hashcode() com coleções

Você também receberá:

  • Diretrizes para uso equals() e hashcode()
  • Regras para fazer comparações de objetos com equals() e hashcode()
  • Erros a evitar ao usar equals() e hashcode()
  • O que lembrar equals() e hashcode()

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á trueentã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 Setentã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 falseo 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 trueisso 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