Respostas às perguntas sobre hashCode() e equals() em "Reprint"

O conteúdo deste capítulo aborda principalmente as seguintes questões:

O que equals() faz ?

Qual é a diferença entre equals() e == ?

Qual é a função de hashCode() ?

Qual é a conexão entre hashCode() e equals()?

5 algumas perguntas comuns em entrevistas sobre HashMap


Parte 1 O papel de iguais()

A função de equals() é  determinar se dois objetos são iguais .

equals() é definido em Object.java do JDK. Distinguir se dois objetos são iguais julgando se seus endereços são iguais (ou seja, se são o mesmo objeto). O código fonte é o seguinte:

public boolean equals(Object obj) { 
    return (this == obj); 
}

Como o método equals() é definido em Object.java, isso significa que todas as classes Java implementam o método equals() e todas as classes podem usar equals() para comparar se dois objetos são iguais. No entanto, já dissemos que usar o método padrão " equals() " é equivalente ao método " == ". Portanto, geralmente reescrevemos o método equals(): se o conteúdo dos dois objetos for igual, o método equals() retorna verdadeiro; caso contrário, retorna fasle.  

De acordo com " se a classe substitui o método equals() ", ela é dividida em 2 categorias.
(01) Se uma classe não substituir o método equals(), ao comparar dois objetos por meio de equals(), ela na verdade compara se os dois objetos são o mesmo objeto. Neste momento, equivale a comparar os dois objetos através de "==".
(02) Podemos substituir o método equals() da classe para permitir que equals() compare se dois objetos são iguais por outros meios. A prática usual é: se o conteúdo dos dois objetos for igual, o método equals() retorna true; caso contrário, retorna fasle.


A seguir, as duas situações acima são descritas com exemplos.

1. "O método equals() não foi substituído"

O código é o seguinte (EqualsTest1.java) :

 Ver código

import java.util.*;
import java.lang.Comparable;

/**
 * @desc equals()的测试程序。
 *
 * @author skywang
 * @emai [email protected]
 */
public class EqualsTest1{

    public static void main(String[] args) {
        // 新建2个相同内容的Person对象,
        // 再用equals比较它们是否相等
        Person p1 = new Person("eee", 100);
        Person p2 = new Person("eee", 100);
        System.out.printf("%s\n", p1.equals(p2));
    }

    /**
     * @desc Person类。
     */
    private static class Person {
        int age;
        String name;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String toString() {
            return name + " - " +age;
        }
    }
}

Resultado da corrida :

falso

Análise de resultados :

       Passamos p1.equals(p2) para "comparar quando p1 e p2 são iguais". Na verdade, o método equals() de Object.java é chamado, ou seja, (p1==p2) é chamado. Está comparando "se p1 e p2 são o mesmo objeto".
       Pela definição de p1 e p2, podemos ver que embora tenham o mesmo conteúdo, são dois objetos diferentes! Portanto, o resultado retornado é falso.

2. O caso de " substituir o método equals() "

Modificamos o EqualsTest1.java acima : substituímos o método equals() .

O código é o seguinte (EqualsTest2.java) :

 Ver código

import java.util.*;
import java.lang.Comparable;

/**
 * @desc equals()的测试程序。
 *
 * @author skywang
 * @emai [email protected]
 */
public class EqualsTest2{

    public static void main(String[] args) {
        // 新建2个相同内容的Person对象,
        // 再用equals比较它们是否相等
        Person p1 = new Person("eee", 100);
        Person p2 = new Person("eee", 100);
        System.out.printf("%s\n", p1.equals(p2));
    }

    /**
     * @desc Person类。
     */
    private static class Person {
        int age;
        String name;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String toString() {
            return name + " - " +age;
        }

        /**
         * @desc 覆盖equals方法
         */
        @Override
        public boolean equals(Object obj){
            if(obj == null){
                return false;
            }

            //如果是同一个对象返回true,反之返回false
            if(this == obj){
                return true;
            }

            //判断是否类型相同
            if(this.getClass() != obj.getClass()){
                return false;
            }

            Person person = (Person)obj;
            return name.equals(person.name) && age==person.age;
        }
    }
}

Resultado da corrida :

verdadeiro

Análise de resultados :

Reescrevemos a função equals() de Person em EqualsTest2.java: quando o nome e a idade de dois objetos Person são iguais, retorne verdadeiro.
Portanto, o resultado da operação retorna verdadeiro.

Falando nisso, aliás, os requisitos do java para equals(). Existem os seguintes pontos:

1. Simetria: Se x.equals(y) retornar "true", então y.equals(x) também deverá retornar "true". 
2. Reflexivo: x.equals(x) deve retornar "true". 
3. Analogia: Se x.equals(y) retornar "true" e y.equals(z) retornar "true", então z.equals(x) também deverá retornar "true". 
4. Consistência: Se x.equals(y) retornar "true", contanto que o conteúdo de xey permaneça inalterado, não importa quantas vezes você repita x.equals(y), o retorno será "true". 
5. Não vazio, x.equals(null), sempre retorna "falso", x.equals (um objeto de tipo diferente de x) sempre retorna "falso".

Agora, vamos revisar a função de equals(): para determinar se dois objetos são iguais. Quando reescrevermos equals(), não altere sua função!


Parte 2 Qual é a diferença entre equals() e ==?

==: Sua função é julgar se os endereços de dois objetos são iguais. Ou seja, julga-se que dois objetos não são o mesmo objeto.

equals(): Sua função também é julgar se dois objetos são iguais. Mas geralmente tem dois casos de uso (a Parte 1 anterior foi apresentada em detalhes):
                 Caso 1, a classe não substitui o método equals(). Então, ao comparar dois objetos desta classe através de equals(), é equivalente a comparar esses dois objetos através de "==".
                 Caso 2, a classe substitui o método equals(). Geralmente, substituímos o método equals() para tornar o conteúdo de dois objetos iguais; se o conteúdo deles for igual, retorne verdadeiro (ou seja, considere os dois objetos iguais).

Abaixo, compare suas diferenças com um exemplo.

O código é o seguinte

 Ver código

import java.util.*;
import java.lang.Comparable;

/**
 * @desc equals()的测试程序。
 *
 * @author skywang
 * @emai [email protected]
 */
public class EqualsTest3{

    public static void main(String[] args) {
        // 新建2个相同内容的Person对象,
        // 再用equals比较它们是否相等
        Person p1 = new Person("eee", 100);
        Person p2 = new Person("eee", 100);
        System.out.printf("p1.equals(p2) : %s\n", p1.equals(p2));
        System.out.printf("p1==p2 : %s\n", p1==p2);
    }

    /**
     * @desc Person类。
     */
    private static class Person {
        int age;
        String name;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String toString() {
            return name + " - " +age;
        }

        /**
         * @desc 覆盖equals方法
         */
        @Override
        public boolean equals(Object obj){
            if(obj == null){
                return false;
            }

            //如果是同一个对象返回true,反之返回false
            if(this == obj){
                return true;
            }

            //判断是否类型相同
            if(this.getClass() != obj.getClass()){
                return false;
            }

            Person person = (Person)obj;
            return name.equals(person.name) && age==person.age;
        }
    }
}

Resultado da corrida :

p1.equals(p2): verdadeiro 
p1==p2: falso

Análise de resultados :

Em EqualsTest3.java:
(01)  p1.equals(p2)
        Isto serve para julgar se o conteúdo de p1 e p2 é igual. Porque Person substitui o método equals() e este equals() é usado para determinar se o conteúdo de p1 e p2 é igual e o conteúdo de p1 e p2 é igual; portanto, retorne verdadeiro.

(02)  p1==p2
       Serve para julgar se p1 e p2 são o mesmo objeto. Como são dois objetos Person recém-criados; portanto, false é retornado.


Parte 3 O papel do hashCode()

A função de hashCode() é obter o código hash , também conhecido como código hash; na verdade, ele retorna um número inteiro int. A função deste código hash é determinar a posição do índice do objeto na tabela hash.

hashCode() é definido em Object.java do JDK, o que significa que qualquer classe em Java contém a função hashCode().
        Embora toda classe Java contenha a função hashCode(). Porém, somente quando uma "tabela hash de uma classe" é criada e usada (veja a descrição abaixo para "tabela hash"), o hashCode() desta classe é útil (a função é: determinar o valor de cada objeto de localização desta classe na tabela hash; em outros casos (por exemplo, criando um único objeto de uma classe ou criando uma matriz de objetos de uma classe, etc.), o hashCode() da classe não tem efeito. O hash tabela acima refere-se a: a classe na coleção Java que é essencialmente uma
       tabela hash, como HashMap, Hashtable, HashSet.

       Ou seja: hashCode() só é útil em tabelas hash e inútil em outros casos. A função de hashCode() na tabela hash é obter o código hash do objeto e, em seguida, determinar a posição do objeto na tabela hash.

OK! Até agora, descobrimos: a função de hashCode() é obter o código hash. Mas para que servem os códigos hash? Por que as tabelas hash precisam de códigos hash? Para resolver esses problemas, você precisa entender as tabelas hash! Quanto ao conteúdo da tabela hash, não fica claro em poucas palavras; você pode aprender através dos seguintes artigos:

[Reimpressão] Hash Table (Hash Table) da teoria à prática (on)

[Reimpressão] Hash Table (Hash Table) da teoria à prática (in)

[Reimpressão] Hash Table (Hash Table) da teoria à prática (Parte 2)

Para entender o conteúdo a seguir, aqui está uma breve introdução à função do código hash.

Todos sabemos que a tabela hash armazena pares de valores-chave (valor-chave), e sua característica é que pode recuperar rapidamente o “valor” correspondente de acordo com a “chave”. É aqui que os códigos hash são usados! 
A essência da tabela hash é realizada por meio do array. Quando queremos obter um “valor” na tabela hash, na verdade queremos obter o elemento em uma determinada posição no array. A posição do array é obtida através da “chave”; além disso, a posição do array é calculada através do código hash correspondente à “chave”.

Abaixo, tomamos HashSet como exemplo para explicar a função de hashCode() em profundidade.

        Suponha que já existam 1.000 elementos no HashSet. O que devo fazer ao inserir o 1001º elemento? Como HashSet é uma coleção Set, ele não permite elementos duplicados.
        “Compare o 1001º elemento um por um com os 1000 elementos anteriores”? Obviamente, esta eficiência é bastante baixa. A tabela hash resolve esse problema muito bem: calcula a posição do elemento na tabela hash de acordo com o código hash do elemento e, em seguida, insere o elemento na posição. Para o mesmo elemento, naturalmente apenas um é salvo.
        Pode-se ver que se dois elementos são iguais, seus códigos hash devem ser iguais; mas o inverso não é necessariamente verdadeiro. Na tabela hash,
                           1. Se dois objetos são iguais, então seus valores hashCode() devem ser iguais;
                           2. Se os hashCode() de dois objetos são iguais, eles não são necessariamente iguais.

                           NOTA: Este é o caso das tabelas hash. Deve ser verdade em não-hashtables!

Para comparar dois objetos, usamos equals() reescritos. Todos equals() e hashCode() têm o seguinte relacionamento

Chega de "o papel do hashCode ()".


Parte 4 A relação entre hashCode() e equals()

A seguir, discutiremos outro tópico. Muitos artigos na Internet associam hashCode() a iguais, alguns dos quais não são completos e são suspeitos de enganar os leitores. Aqui, resolvi sozinho a "relação entre hashCode() e equals()".

Usamos o “ propósito da classe ” para explicar a “relação entre hashCode() e equals()” em dois casos.

1. O primeiro tipo não criará uma "tabela hash correspondente à classe"

         O ditado "não criaremos uma tabela hash correspondente à classe" aqui significa: Não usaremos esta classe em estruturas de dados como HashSet, Hashtable, HashMap, etc., que são essencialmente tabelas hash. Por exemplo, uma coleção HashSet desta classe não será criada.

        Neste caso, os "hashCode() e equals()" desta classe não têm nada a ver um com o outro!
        Neste caso, equals() é usado para comparar dois objetos da classe quanto à igualdade. E hashCode() não tem efeito algum, então ignore hashCode().

Abaixo, utilizamos um exemplo para ver o valor de hashCode() quando dois objetos da classe são iguais  ou  não .

O código-fonte é o seguinte  (NormalHashCodeTest.java):

 Ver código

import java.util.*;
import java.lang.Comparable;

/**
 * @desc 比较equals() 返回true 以及 返回false时, hashCode()的值。
 *
 * @author skywang
 * @emai [email protected]
 */
public class NormalHashCodeTest{

    public static void main(String[] args) {
        // 新建2个相同内容的Person对象,
        // 再用equals比较它们是否相等
        Person p1 = new Person("eee", 100);
        Person p2 = new Person("eee", 100);
        Person p3 = new Person("aaa", 200);
        System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
        System.out.printf("p1.equals(p3) : %s; p1(%d) p3(%d)\n", p1.equals(p3), p1.hashCode(), p3.hashCode());
    }

    /**
     * @desc Person类。
     */
    private static class Person {
        int age;
        String name;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String toString() {
            return name + " - " +age;
        }

        /**
         * @desc 覆盖equals方法
         */
        public boolean equals(Object obj){
            if(obj == null){
                return false;
            }

            //如果是同一个对象返回true,反之返回false
            if(this == obj){
                return true;
            }

            //判断是否类型相同
            if(this.getClass() != obj.getClass()){
                return false;
            }

            Person person = (Person)obj;
            return name.equals(person.name) && age==person.age;
        }
    }
}

Resultado da corrida :

p1.equals(p2): verdadeiro; p1(1169863946) p2(1901116749) 
p1.equals(p3) : falso; p1(1169863946)p3(2131949076)

Também pode ser visto pelos resultados: quando p1 e p2 são iguais, hashCode() não é necessariamente igual.

2. O segundo criará uma "tabela hash correspondente à classe"

        A "tabela hash correspondente à classe" mencionada aqui significa: usaremos esta classe em HashSet, Hashtable, HashMap, etc., que são estruturas de dados que são essencialmente tabelas hash. Por exemplo, uma coleção HashSet desta classe será criada.

        Neste caso, os "hashCode() e equals()" desta classe estão relacionados:
        1) Se dois objetos são iguais, então seus valores de hashCode() devem ser iguais.
              A igualdade aqui se refere ao retorno verdadeiro ao comparar dois objetos por meio de equals().
        2) Se o hashCode() de dois objetos for igual, eles não são necessariamente iguais.
               Porque na tabela hash, hashCode() é igual, ou seja, os valores hash dos dois pares de valores-chave são iguais. No entanto, se os valores de hash forem iguais, isso não significa necessariamente que os pares de valores-chave sejam iguais. Adicione uma frase: "Dois pares de valores-chave diferentes, o valor do hash é igual", esta é uma colisão de hash.

        Além disso, neste caso. Para determinar se dois objetos são iguais, além de substituir equals(), a função hashCode() também deve ser substituída. Caso contrário, equals() não terá efeito.
Por exemplo, para criar uma coleção HashSet da classe Person, os métodos equals() e hashCode() da classe Person devem ser substituídos ao mesmo tempo.
        Se você apenas substituir o método equals(). Descobriremos que o método equals() não atinge o efeito que desejamos.

Código de referência  (ConflictHashCodeTest1.java):

 Ver código

import java.util.*;
import java.lang.Comparable;

/**
 * @desc 比较equals() 返回true 以及 返回false时, hashCode()的值。
 *
 * @author skywang
 * @emai [email protected]
 */
public class ConflictHashCodeTest1{

    public static void main(String[] args) {
        // 新建Person对象,
        Person p1 = new Person("eee", 100);
        Person p2 = new Person("eee", 100);
        Person p3 = new Person("aaa", 200);

        // 新建HashSet对象
        HashSet set = new HashSet();
        set.add(p1);
        set.add(p2);
        set.add(p3);

        // 比较p1 和 p2, 并打印它们的hashCode()
        System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
        // 打印set
        System.out.printf("set:%s\n", set);
    }

    /**
     * @desc Person类。
     */
    private static class Person {
        int age;
        String name;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String toString() {
            return "("+name + ", " +age+")";
        }

        /**
         * @desc 覆盖equals方法
         */
        @Override
        public boolean equals(Object obj){
            if(obj == null){
                return false;
            }

            //如果是同一个对象返回true,反之返回false
            if(this == obj){
                return true;
            }

            //判断是否类型相同
            if(this.getClass() != obj.getClass()){
                return false;
            }

            Person person = (Person)obj;
            return name.equals(person.name) && age==person.age;
        }
    }
}

Resultado da corrida :

p1.equals(p2): verdadeiro; p1(1169863946) p2(1690552137) 
conjunto:[(eee, 100), (eee, 100), (aaa, 200)]

Análise de resultados :

        Substituímos equals() de Person. Porém, uma descoberta muito estranha: ainda existem elementos duplicados no HashSet: p1 e p2. Por que isso acontece?

        Isso ocorre porque embora o conteúdo de p1 e p2 seja igual, seu hashCode() não é igual; portanto, quando HashSet adiciona p1 e p2, eles são considerados desiguais.

Abaixo, substituímos os métodos equals() e hashCode().

Código de referência (ConflictHashCodeTest2.java):

 Ver código

import java.util.*;
import java.lang.Comparable;

/**
 * @desc 比较equals() 返回true 以及 返回false时, hashCode()的值。
 *
 * @author skywang
 * @emai [email protected]
 */
public class ConflictHashCodeTest2{

    public static void main(String[] args) {
        // 新建Person对象,
        Person p1 = new Person("eee", 100);
        Person p2 = new Person("eee", 100);
        Person p3 = new Person("aaa", 200);
        Person p4 = new Person("EEE", 100);

        // 新建HashSet对象
        HashSet set = new HashSet();
        set.add(p1);
        set.add(p2);
        set.add(p3);

        // 比较p1 和 p2, 并打印它们的hashCode()
        System.out.printf("p1.equals(p2) : %s; p1(%d) p2(%d)\n", p1.equals(p2), p1.hashCode(), p2.hashCode());
        // 比较p1 和 p4, 并打印它们的hashCode()
        System.out.printf("p1.equals(p4) : %s; p1(%d) p4(%d)\n", p1.equals(p4), p1.hashCode(), p4.hashCode());
        // 打印set
        System.out.printf("set:%s\n", set);
    }

    /**
     * @desc Person类。
     */
    private static class Person {
        int age;
        String name;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String toString() {
            return name + " - " +age;
        }

        /**
         * @desc重写hashCode
         */
        @Override
        public int hashCode(){
            int nameHash =  name.toUpperCase().hashCode();
            return nameHash ^ age;
        }

        /**
         * @desc 覆盖equals方法
         */
        @Override
        public boolean equals(Object obj){
            if(obj == null){
                return false;
            }

            //如果是同一个对象返回true,反之返回false
            if(this == obj){
                return true;
            }

            //判断是否类型相同
            if(this.getClass() != obj.getClass()){
                return false;
            }

            Person person = (Person)obj;
            return name.equals(person.name) && age==person.age;
        }
    }
}

Resultado da corrida :

p1.equals(p2): verdadeiro; p1(68545) p2(68545) 
p1.equals(p4) : falso; p1(68545) p4(68545) 
conjunto:[aaa - 200, eee - 100]

Análise de resultados :

        Agora, equals() entra em vigor e não há elementos duplicados no HashSet.
        Comparando p1 e p2 , descobrimos que seu hashCode() é igual, e compará-los por meio de equals() também retorna verdadeiro. Portanto, p1 e p2 são considerados iguais.
        Comparando p1 e p4 , descobrimos que embora seus hashCode() sejam iguais; no entanto, compará-los através de equals() retorna falso. Portanto, p1 e p4 são considerados desiguais.

Questões de entrevista

 um

dois, 

Acho que você gosta

Origin blog.csdn.net/weixin_61061381/article/details/125827945
Recomendado
Clasificación