简体   繁体   English

为什么我需要覆盖 Java 中的 equals 和 hashCode 方法?

[英]Why do I need to override the equals and hashCode methods in Java?

Recently I read through this Developer Works Document .最近我通读了这个Developer Works 文档

The document is all about defining hashCode() and equals() effectively and correctly, however I am not able to figure out why we need to override these two methods.该文档是关于有效且正确地定义hashCode()equals() ,但是我无法弄清楚为什么我们需要覆盖这两个方法。

How can I take the decision to implement these methods efficiently?我如何才能决定有效地实施这些方法?

Joshua Bloch says on Effective Java Joshua Bloch 谈到 Effective Java

You must override hashCode() in every class that overrides equals().您必须在每个覆盖 equals() 的类中覆盖 hashCode()。 Failure to do so will result in a violation of the general contract for Object.hashCode(), which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable.不这样做将导致违反 Object.hashCode() 的一般契约,这将阻止您的类与所有基于哈希的集合(包括 HashMap、HashSet 和 Hashtable)一起正常运行。

Let's try to understand it with an example of what would happen if we override equals() without overriding hashCode() and attempt to use a Map .让我们尝试通过一个例子来理解它,如果我们覆盖equals()而不覆盖hashCode()并尝试使用Map会发生什么。

Say we have a class like this and that two objects of MyClass are equal if their importantField is equal (with hashCode() and equals() generated by eclipse)假设我们有一个这样的类,如果MyClass两个对象的importantField相等,则它们相等(使用 eclipse 生成的hashCode()equals()

public class MyClass {
    private final String importantField;
    private final String anotherField;

    public MyClass(final String equalField, final String anotherField) {
        this.importantField = equalField;
        this.anotherField = anotherField;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((importantField == null) ? 0 : importantField.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final MyClass other = (MyClass) obj;
        if (importantField == null) {
            if (other.importantField != null)
                return false;
        } else if (!importantField.equals(other.importantField))
            return false;
        return true;
    }
}

Imagine you have this想象一下你有这个

MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");

Override only equals覆盖仅equals

If only equals is overriden, then when you call myMap.put(first,someValue) first will hash to some bucket and when you call myMap.put(second,someOtherValue) it will hash to some other bucket (as they have a different hashCode ).如果只覆盖equals ,那么当您myMap.put(first,someValue)调用myMap.put(first,someValue)将散列到某个桶,当您调用myMap.put(second,someOtherValue) ,它将散列到另一个桶(因为它们具有不同的hashCode )。 So, although they are equal, as they don't hash to the same bucket, the map can't realize it and both of them stay in the map.因此,尽管它们相等,但由于它们没有散列到同一个桶中,因此地图无法意识到这一点,并且它们都留在地图中。


Although it is not necessary to override equals() if we override hashCode() , let's see what would happen in this particular case where we know that two objects of MyClass are equal if their importantField is equal but we do not override equals() .虽然如果我们覆盖hashCode()则没有必要覆盖equals() ,让我们看看在这种特殊情况下会发生什么,我们知道MyClass两个对象如果它们的importantField相等,但我们没有覆盖equals()

Override only hashCode仅覆盖hashCode

If you only override hashCode then when you call myMap.put(first,someValue) it takes first, calculates its hashCode and stores it in a given bucket.如果您只覆盖hashCode那么当您调用myMap.put(first,someValue) ,它将首先计算其hashCode并将其存储在给定的存储桶中。 Then when you call myMap.put(second,someOtherValue) it should replace first with second as per the Map Documentation because they are equal (according to the business requirement).然后,当您调用myMap.put(second,someOtherValue)它应该根据地图文档将 first 替换为 second,因为它们是相等的(根据业务需求)。

But the problem is that equals was not redefined, so when the map hashes second and iterates through the bucket looking if there is an object k such that second.equals(k) is true it won't find any as second.equals(first) will be false .但问题是 equals 没有被重新定义,所以当映射散列second并遍历存储桶寻找是否有对象k使得second.equals(k)为真时,它不会找到任何作为second.equals(first)将是false

Hope it was clear希望很清楚

Collections such as HashMap and HashSet use a hashcode value of an object to determine how it should be stored inside a collection, and the hashcode is used again in order to locate the object in its collection.诸如HashMapHashSet集合使用对象的哈希码值来确定它应该如何存储在集合中,并且再次使用哈希码以便在其集合中定位该对象。

Hashing retrieval is a two-step process:哈希检索是一个两步过程:

  1. Find the right bucket (using hashCode() )找到正确的存储桶(使用hashCode()
  2. Search the bucket for the right element (using equals() )在桶中搜索正确的元素(使用equals()

Here is a small example on why we should overrride equals() and hashcode() .这是一个关于为什么我们应该覆盖equals()hashcode()的小例子。

Consider an Employee class which has two fields: age and name.考虑一个Employee类,它有两个字段:年龄和姓名。

public class Employee {

    String name;
    int age;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;
        if (!(obj instanceof Employee))
            return false;
        Employee employee = (Employee) obj;
        return employee.getAge() == this.getAge()
                && employee.getName() == this.getName();
    }

    // commented    
    /*  @Override
        public int hashCode() {
            int result=17;
            result=31*result+age;
            result=31*result+(name!=null ? name.hashCode():0);
            return result;
        }
     */
}

Now create a class, insert Employee object into a HashSet and test whether that object is present or not.现在创建一个类,将Employee对象插入HashSet并测试该对象是否存在。

public class ClientTest {
    public static void main(String[] args) {
        Employee employee = new Employee("rajeev", 24);
        Employee employee1 = new Employee("rajeev", 25);
        Employee employee2 = new Employee("rajeev", 24);

        HashSet<Employee> employees = new HashSet<Employee>();
        employees.add(employee);
        System.out.println(employees.contains(employee2));
        System.out.println("employee.hashCode():  " + employee.hashCode()
        + "  employee2.hashCode():" + employee2.hashCode());
    }
}

It will print the following:它将打印以下内容:

false
employee.hashCode():  321755204  employee2.hashCode():375890482

Now uncomment hashcode() method , execute the same and the output would be:现在取消注释hashcode()方法,执行相同的输出将是:

true
employee.hashCode():  -938387308  employee2.hashCode():-938387308

Now can you see why if two objects are considered equal, their hashcode s must also be equal?现在你能明白为什么如果两个对象被认为是相等的,它们的hashcode也必须相等吗? Otherwise, you'd never be able to find the object since the default hashcode method in class Object virtually always comes up with a unique number for each object, even if the equals() method is overridden in such a way that two or more objects are considered equal.否则,您将永远无法找到该对象,因为 Object 类中的默认hashcode方法实际上总是为每个对象提供一个唯一编号,即使equals()方法以两个或多个对象的方式被覆盖被认为是平等的。 It doesn't matter how equal the objects are if their hashcode s don't reflect that.如果对象的hashcode没有反映出来,则对象的相等程度无关紧要。 So one more time: If two objects are equal, their hashcode s must be equal as well.再说一遍:如果两个对象相等,则它们的hashcode 也必须相等。

You must override hashCode() in every class that overrides equals().您必须在每个覆盖 equals() 的类中覆盖 hashCode()。 Failure to do so will result in a violation of the general contract for Object.hashCode(), which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable.不这样做将导致违反 Object.hashCode() 的一般契约,这将阻止您的类与所有基于哈希的集合(包括 HashMap、HashSet 和 Hashtable)一起正常运行。


from Effective Java , by Joshua Bloch来自Effective Java ,作者 Joshua Bloch

By defining equals() and hashCode() consistently, you can improve the usability of your classes as keys in hash-based collections.通过一致地定义equals()hashCode() ,您可以提高类作为基于哈希的集合中的键的可用性。 As the API doc for hashCode explains: "This method is supported for the benefit of hashtables such as those provided by java.util.Hashtable ."正如 hashCode 的 API 文档所解释的那样:“为了哈希表(例如java.util.Hashtable提供的哈希表)的好处,支持此方法。”

The best answer to your question about how to implement these methods efficiently is suggesting you to read Chapter 3 of Effective Java .关于如何有效地实现这些方法的问题的最佳答案是建议您阅读Effective Java 的第 3 章。

Identity is not equality.身份不是平等。

  • equals operator == test identity.等于运算符==测试身份。
  • equals(Object obj) method compares equality test(ie we need to tell equality by overriding the method) equals(Object obj)方法比较相等性测试(即我们需要通过覆盖该方法来告诉相等性)

Why do I need to override the equals and hashCode methods in Java?为什么我需要覆盖 Java 中的 equals 和 hashCode 方法?

First we have to understand the use of equals method.首先我们要了解equals方法的使用。

In order to identity differences between two objects we need to override equals method.为了识别两个对象之间的差异,我们需要覆盖 equals 方法。

For example:例如:

Customer customer1=new Customer("peter");
Customer customer2=customer1;
customer1.equals(customer2); // returns true by JVM. i.e. both are refering same Object
------------------------------
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
customer1.equals(customer2); //return false by JVM i.e. we have two different peter customers.

------------------------------
Now I have overriden Customer class equals method as follows:
 @Override
    public boolean equals(Object obj) {
        if (this == obj)   // it checks references
            return true;
        if (obj == null) // checks null
            return false;
        if (getClass() != obj.getClass()) // both object are instances of same class or not
            return false;
        Customer other = (Customer) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name)) // it again using bulit in String object equals to identify the difference 
            return false;
        return true; 
    }
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
Insteady identify the Object equality by JVM, we can do it by overring equals method.
customer1.equals(customer2);  // returns true by our own logic

Now hashCode method can understand easily.现在 hashCode 方法可以很容易理解了。

hashCode produces integer in order to store object in data structures like HashMap , HashSet . hashCode 生成整数,以便将对象存储在HashMapHashSet等数据结构中。

Assume we have override equals method of Customer as above,假设我们已经覆盖了上面的Customer方法,

customer1.equals(customer2);  // returns true by our own logic

While working with data structure when we store object in buckets(bucket is a fancy name for folder).当我们将对象存储在存储桶中时处理数据结构时(存储桶是文件夹的奇特名称)。 If we use built-in hash technique, for above two customers it generates two different hashcode.如果我们使用内置哈希技术,对于以上两个客户,它会生成两个不同的哈希码。 So we are storing the same identical object in two different places.所以我们在两个不同的地方存储相同的对象。 To avoid this kind of issues we should override the hashCode method also based on the following principles.为避免此类问题,我们还应根据以下原则覆盖 hashCode 方法。

  • un-equal instances may have same hashcode.不相等的实例可能具有相同的哈希码。
  • equal instances should return same hashcode.相等的实例应该返回相同的哈希码。

Simply put, the equals-method in Object check for reference equality, where as two instances of your class could still be semantically equal when the properties are equal.简单地说,Object 中的 equals 方法检查引用是否相等,因为当属性相等时,类的两个实例在语义上仍然是相等的。 This is for instance important when putting your objects into a container that utilizes equals and hashcode, like HashMap and Set .例如,当将对象放入使用 equals 和 hashcode 的容器(如HashMapSet )时,这一点很重要。 Let's say we have a class like:假设我们有一个类:

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }
}

We create two instances with the same id :我们创建两个具有相同id 的实例:

Foo a = new Foo("id", "something");
Foo b = new Foo("id", "something else");

Without overriding equals we are getting:在不覆盖 equals 的情况下,我们得到:

  • a.equals(b) is false because they are two different instances a.equals(b) 是假的,因为它们是两个不同的实例
  • a.equals(a) is true since it's the same instance a.equals(a) 为真,因为它是同一个实例
  • b.equals(b) is true since it's the same instance b.equals(b) 为真,因为它是同一个实例

Correct?正确的? Well maybe, if this is what you want.好吧,如果这是你想要的。 But let's say we want objects with the same id to be the same object, regardless if it's two different instances.但是假设我们希望具有相同 id 的对象是同一个对象,无论它是否是两个不同的实例。 We override the equals (and hashcode):我们覆盖等号(和哈希码):

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Foo) {
            return ((Foo)other).id.equals(this.id);   
        }
    }

    @Override
    public int hashCode() {
        return this.id.hashCode();
    }
}

As for implementing equals and hashcode I can recommend using Guava's helper methods至于实现 equals 和 hashcode,我可以推荐使用Guava 的辅助方法

Why we override equals() method为什么我们重写equals()方法

In Java we can not overload how operators like ==, +=, -+ behave.在 Java 中,我们不能重载 ==、+=、-+ 等运算符的行为方式。 They are behaving a certain way.他们以某种方式行事。 So let's focus on the operator == for our case here.因此,让我们在这里关注我们的案例中的运算符 ==。

How operator == works.运算符 == 的工作原理。

It checks if 2 references that we compare point to the same instance in memory.它检查我们比较的 2 个引用是否指向内存中的同一个实例。 Operator == will resolve to true only if those 2 references represent the same instance in memory.仅当这 2 个引用代表内存中的同一实例时,运算符==才会解析为 true。

So now let's consider the following example所以现在让我们考虑下面的例子

public class Person {

      private Integer age;
      private String name;
    
      ..getters, setters, constructors
      }

So let's say that in your program you have built 2 Person objects on different places and you wish to compare them.因此,假设在您的程序中,您在不同的地方构建了 2 个 Person 对象,并且您希望对它们进行比较。

Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println ( person1 == person2 );  --> will print false!

Those 2 objects from business perspective look the same right?从业务角度来看,这两个对象看起来一样吗? For JVM they are not the same.对于 JVM,它们是不一样的。 Since they are both created with new keyword those instances are located in different segments in memory.由于它们都是使用new关键字创建的,因此这些实例位于内存中的不同段中。 Therefore the operator == will return false因此运算符 == 将返回false

But if we can not override the == operator how can we say to JVM that we want those 2 objects to be treated as same.但是如果我们不能覆盖 == 操作符,我们怎么能告诉 JVM 我们希望这两个对象被视为相同。 There comes the .equals() method in play..equals()方法在起作用。

You can override equals() to check if some objects have same values for specific fields to be considered equal.您可以覆盖equals()以检查某些对象是否具有相同的特定字段的值被视为相等。

You can select which fields you want to be compared.您可以选择要比较的字段。 If we say that 2 Person objects will be the same if and only if they have the same age and same name, then the IDE will create something like the following for automatic generation of equals()如果我们说 2 Person 对象是相同的当且仅当它们具有相同的年龄和相同的名称,那么 IDE 将创建类似以下内容以自动生成 equals()

@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }

Let's go back to our previous example让我们回到我们之前的例子

    Person person1 = new Person("Mike", 34);
    Person person2 = new Person("Mike", 34);
    System.out.println ( person1 == person2 );   --> will print false!
    System.out.println ( person1.equals(person2) );  --> will print true!

So we can not overload == operator to compare objects the way we want but Java gave us another way, the equals() method, which we can override as we want.所以我们不能重载 == 运算符以我们想要的方式比较对象,但 Java 给了我们另一种方式, equals()方法,我们可以根据需要覆盖它。

Keep in mind however, if we don't provide our custom version of .equals() (aka override) in our class then the predefined .equals() from Object class and == operator will behave exactly the same.但是请记住,如果我们不在类中提供自定义版本的.equals() (又名覆盖),那么来自 Object 类的预定义.equals()==运算符的行为将完全相同。

Default equals() method which is inherited from Object will check whether both compared instances are the same in memory!从 Object 继承的默认equals()方法将检查两个比较的实例在内存中是否相同!

Why we override hashCode() method为什么我们重写hashCode()方法

Some Data Structures in java like HashSet, HashMap store their elements based on a hash function which is applied on those elements. java中的一些数据结构如HashSet、HashMap基于应用于这些元素的散列函数来存储它们的元素。 The hashing function is the hashCode()散列函数是hashCode()

If we have a choice of overriding .equals() method then we must also have a choice of overriding hashCode() method.如果我们可以选择覆盖.equals()方法,那么我们也必须选择覆盖hashCode()方法。 There is a reason for that.这是有原因的。

Default implementation of hashCode() which is inherited from Object considers all objects in memory unique!从 Object 继承的hashCode()默认实现认为内存中的所有对象都是唯一的!

Let's get back to those hash data structures.让我们回到那些散列数据结构。 There is a rule for those data structures.这些数据结构有一个规则。

HashSet can not contain duplicate values and HashMap can not contain duplicate keys HashSet 不能包含重复值,HashMap 不能包含重复键

HashSet is implemented with a HashMap behind the scenes where each value of a HashSet is stored as a key in a HashMap. HashSet 在幕后使用 HashMap 实现,其中 HashSet 的每个值都作为键存储在 HashMap 中。

So we have to understand how a HashMap works.所以我们必须了解 HashMap 是如何工作的。

In a simple way a HashMap is a native array that has some buckets.简单来说,HashMap 是一个具有一些桶的本机数组。 Each bucket has a linkedList.每个桶都有一个链表。 In that linkedList our keys are stored.在那个链表中,我们的密钥被存储。 HashMap locates the correct linkedList for each key by applying hashCode() method and after that it iterates through all elements of that linkedList and applies equals() method on each of these elements to check if that element is already contained there. HashMap 通过应用hashCode()方法为每个键定位正确的链表,然后它遍历该链表的所有元素并对这些元素中的每一个应用equals()方法以检查该元素是否已经包含在那里。 No duplicate keys are allowed.不允许重复键。

在此处输入图片说明

When we put something inside a HashMap, the key is stored in one of those linkedLists.当我们在 HashMap 中放入一些东西时,键存储在这些链表之一中。 In which linkedList that key will be stored is shown by the result of hashCode() method on that key.该键将存储在哪个链表中,由该键上的hashCode()方法的结果显示。 So if key1.hashCode() has as a result 4, then that key1 will be stored on the 4th bucket of the array, in the linkedList that exists there.因此,如果key1.hashCode()结果为 4,则该 key1 将存储在数组的第 4 个存储桶中,即存在于那里的链表中。

By default hashCode() method returns a different result for each different instance.默认情况下, hashCode()方法为每个不同的实例返回不同的结果。 If we have the default equals() which behaves like == which considers all instances in memory as different objects we don't have any problem.如果我们有默认的equals() ,它的行为类似于 == ,它将内存中的所有实例视为不同的对象,我们没有任何问题。

But in our previous example we said we want Person instances to be considered equal if their ages and names match.但是在我们之前的示例中,我们说过如果他们的年龄和姓名匹配,我们希望 Person 实例被认为是平等的。

    Person person1 = new Person("Mike", 34);
    Person person2 = new Person("Mike", 34);
    System.out.println ( person1.equals(person2) );  --> will print true!

Now let's create a map to store those instances as keys with some string as pair value现在让我们创建一个映射来存储这些实例作为键,一些字符串作为对值

Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");

In Person class we have not overridden the hashCode method but we have overridden equals method.在 Person 类中,我们没有覆盖hashCode方法,但我们覆盖了equals方法。 Since the default hashCode provides different results for different java instances person1.hashCode() and person2.hashCode() have big chances of having different results.由于默认的hashCode为不同的 java 实例提供了不同的结果, person1.hashCode()person2.hashCode()有很大的机会得到不同的结果。

Our map might end with those persons in different linkedLists.我们的地图可能以不同链表中的那些人结束。

在此处输入图片说明

This is against the logic of a HashMap这违反了 HashMap 的逻辑

A HashMap is not allowed to have multiple equal keys!一个 HashMap 不允许有多个相等的键!

But ours now has and the reason is that the default hashCode() which was inherited from Object Class was not enough.但是我们现在有了,原因是从对象类继承的默认hashCode()是不够的。 Not after we have overridden the equals() method on Person Class.不是在我们覆盖 Person 类的equals()方法之后。

That is the reason why we must override hashCode() method after we have overridden equals method.这就是为什么我们必须在重写equals方法后重写hashCode()方法的原因。

Now let's fix that.现在让我们解决这个问题。 Let's override our hashCode() method to consider the same fields that equals() considers, namely age, name让我们重写我们的hashCode()方法来考虑equals()考虑的相同字段,即age, name

 public class Person {

      private Integer age;
      private String name;
    
      ..getters, setters, constructors

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

      }

Now let's try again to save those keys in our HashMap现在让我们再次尝试将这些键保存在我们的 HashMap 中

Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");

person1.hashCode() and person2.hashCode() will definitely be the same. person1.hashCode()person2.hashCode()肯定是一样的。 Let's say it is 0.假设它是0。

HashMap will go to bucket 0 and in that LinkedList will save the person1 as key with the value "1". HashMap 将转到存储桶 0,在该 LinkedList 中,将 person1 保存为值为“1”的键。 For the second put HashMap is intelligent enough and when it goes again to bucket 0 to save person2 key with value "2" it will see that another equal key already exists there.对于第二个 put HashMap 足够智能,当它再次进入存储桶 0 以保存值为“2”的 person2 键时,它将看到另一个相等的键已经存在于那里。 So it will overwrite the previous key.所以它会覆盖以前的密钥。 So in the end only person2 key will exist in our HashMap.所以最终只有 person2 键存在于我们的 HashMap 中。

在此处输入图片说明

Now we are aligned with the rule of Hash Map that says no multiple equal keys are allowed!现在我们符合 Hash Map 的规则,即不允许多个相等的键!

hashCode() : hashCode()

If you only override the hash-code method nothing happens, because it always returns a new hashCode for each object as an Object class.如果你只覆盖 hash-code 方法什么也不会发生,因为它总是为每个对象返回一个新的hashCode作为 Object 类。

equals() : equals()

If you only override the equals method, if a.equals(b) is true it means the hashCode of a and b must be the same but that does not happen since you did not override the hashCode method.如果您只覆盖 equals 方法,如果a.equals(b)为真,则意味着 a 和 b 的hashCode必须相同,但由于您没有覆盖hashCode方法,因此不会发生这种情况。

Note : hashCode() method of Object class always returns a new hashCode for each object.注意: Object 类的hashCode()方法总是为每个对象返回一个新的hashCode

So when you need to use your object in the hashing based collection, you must override both equals() and hashCode() .因此,当您需要在基于散列的集合中使用您的对象时,您必须同时覆盖equals()hashCode()

Let me explain the concept in very simple words.让我用非常简单的词来解释这个概念。

Firstly from a broader perspective we have collections, and hashmap is one of the datastructure in the collections.首先从更广泛的角度来看,我们有集合,而 hashmap 是集合中的数据结构之一。

To understand why we have to override the both equals and hashcode method, if need to first understand what is hashmap and what is does.要理解为什么我们必须覆盖 equals 和 hashcode 方法,首先需要了解什么是 hashmap 和什么是。

A hashmap is a datastructure which stores key value pairs of data in array fashion.哈希图是一种以数组方式存储数据的键值对的数据结构。 Lets say a[], where each element in 'a' is a key value pair.假设 a[],其中 'a' 中的每个元素都是一个键值对。

Also each index in the above array can be linked list thereby having more than one values at one index.此外,上述数组中的每个索引都可以是链表,从而在一个索引处具有多个值。

Now why is a hashmap used?现在为什么要使用哈希图?

If we have to search among a large array then searching through each if them will not be efficient, so what hash technique tells us that lets pre process the array with some logic and group the elements based on that logic ie Hashing如果我们必须在一个大数组中搜索,那么搜索每个数组将不会有效,那么散列技术告诉我们,让我们用一些逻辑预处理数组并根据该逻辑对元素进行分组,即散列

EG: we have array 1,2,3,4,5,6,7,8,9,10,11 and we apply a hash function mod 10 so 1,11 will be grouped in together. EG:我们有数组 1,2,3,4,5,6,7,8,9,10,11 并且我们应用了一个哈希函数 mod 10 所以 1,11 将被分组在一起。 So if we had to search for 11 in previous array then we would have to iterate the complete array but when we group it we limit our scope of iteration thereby improving speed.因此,如果我们必须在前一个数组中搜索 11,那么我们将不得不迭代整个数组,但是当我们对其进行分组时,我们限制了迭代范围,从而提高了速度。 That datastructure used to store all the above information can be thought of as a 2d array for simplicity为简单起见,用于存储所有上述信息的数据结构可以被认为是一个二维数组

Now apart from the above hashmap also tells that it wont add any Duplicates in it.现在除了上面的 hashmap 还告诉它不会在其中添加任何重复项。 And this is the main reason why we have to override the equals and hashcode这就是我们必须覆盖 equals 和 hashcode 的主要原因

So when its said that explain the internal working of hashmap , we need to find what methods the hashmap has and how does it follow the above rules which i explained above所以当它说解释hashmap的内部工作时,我们需要找到hashmap有哪些方法以及它如何遵循我上面解释的上述规则

so the hashmap has method called as put(K,V) , and according to hashmap it should follow the above rules of efficiently distributing the array and not adding any duplicates所以 hashmap 有称为 put(K,V) 的方法,并且根据 hashmap 它应该遵循有效分布数组的上述规则,并且不添加任何重复项

so what put does is that it will first generate the hashcode for the given key to decide which index the value should go in.if nothing is present at that index then the new value will be added over there, if something is already present over there then the new value should be added after the end of the linked list at that index.所以 put 的作用是首先为给定的键生成哈希码来决定该值应该放在哪个索引中。那么新值应该添加到该索引的链表末尾之后。 but remember no duplicates should be added as per the desired behavior of the hashmap.但请记住,不应根据哈希图的所需行为添加重复项。 so lets say you have two Integer objects aa=11,bb=11.所以假设你有两个整数对象 aa=11,bb=11。

As every object derived from the object class, the default implementation for comparing two objects is that it compares the reference and not values inside the object.作为从对象类派生的每个对象,比较两个对象的默认实现是比较对象内部的引用而不是值。 So in the above case both though semantically equal will fail the equality test, and possibility that two objects which same hashcode and same values will exists thereby creating duplicates.因此,在上述情况下,尽管语义上相等,但相等性测试将失败,并且存在具有相同哈希码和相同值的两个对象从而创建重复项的可能性。 If we override then we could avoid adding duplicates.如果我们覆盖,那么我们可以避免添加重复项。 You could also refer to Detail working您也可以参考 详细工作

import java.util.HashMap;


public class Employee {
    String name;
    String mobile;

    public Employee(String name,String mobile) {
        this.name = name;
        this.mobile = mobile;
    }
    
    @Override
    public int hashCode() {
        System.out.println("calling hascode method of Employee");
        String str = this.name;
        int sum = 0;
        for (int i = 0; i < str.length(); i++) {
            sum = sum + str.charAt(i);
        }
        return sum;
    }

    @Override
    public boolean equals(Object obj) {
        // TODO Auto-generated method stub
        System.out.println("calling equals method of Employee");
        Employee emp = (Employee) obj;
        if (this.mobile.equalsIgnoreCase(emp.mobile)) {
            System.out.println("returning true");
            return true;
        } else {
            System.out.println("returning false");
            return false;
        }
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Employee emp = new Employee("abc", "hhh");
        Employee emp2 = new Employee("abc", "hhh");
        HashMap<Employee, Employee> h = new HashMap<>();
        //for (int i = 0; i < 5; i++) {
            h.put(emp, emp);
            h.put(emp2, emp2);
        //}
        
        System.out.println("----------------");
        System.out.println("size of hashmap: "+h.size());
    }
}

Java puts a rule that Java 提出了一个规则,即

"If two objects are equal using Object class equals method, then the hashcode method should give the same value for these two objects." “如果两个对象使用 Object class equals 方法相等,那么 hashcode 方法应该为这两个对象提供相同的值。”

So, if in our class we override equals() we should override hashcode() method also to follow this rule.所以,如果在我们的类中我们覆盖了equals()我们也应该覆盖hashcode()方法来遵循这个规则。 Both methods, equals() and hashcode() , are used in Hashtable , for example, to store values as key-value pairs.例如,在Hashtable中使用equals()hashcode()两种方法将值存储为键值对。 If we override one and not the other, there is a possibility that the Hashtable may not work as we want, if we use such object as a key.如果我们覆盖一个而不是另一个,如果我们使用这样的对象作为键, Hashtable可能无法按我们想要的方式工作。

Because if you do not override them you will be use the default implentation in Object.因为如果您不覆盖它们,您将使用 Object 中的默认实现。

Given that instance equality and hascode values generally require knowledge of what makes up an object they generally will need to be redefined in your class to have any tangible meaning.鉴于实例相等和 hascode 值通常需要了解组成对象的内容,它们通常需要在您的类中重新定义以具有任何有形的含义。

In order to use our own class objects as keys in collections like HashMap, Hashtable etc.. , we should override both methods ( hashCode() and equals() ) by having an awareness on internal working of collection.为了使用我们自己的类对象作为 HashMap、Hashtable 等集合中的键,我们应该通过了解集合的内部工作来覆盖这两个方法( hashCode() 和 equals() )。 Otherwise, it leads to wrong results which we are not expected.否则,它会导致我们意想不到的错误结果。

Adding to @Lombo 's answer添加到@Lombo 的答案

When will you need to override equals() ?什么时候需要覆盖 equals() ?

The default implementation of Object's equals() is Object 的 equals() 的默认实现是

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

which means two objects will be considered equal only if they have the same memory address which will be true only if you are comparing an object with itself.这意味着两个对象只有在它们具有相同的内存地址时才会被认为是相等的,只有当您将一个对象与其自身进行比较时才为真。

But you might want to consider two objects the same if they have the same value for one or more of their properties (Refer the example given in @Lombo 's answer).但是,如果两个对象的一个​​或多个属性具有相同的值,您可能希望将它们视为相同的对象(请参阅@Lombo 的回答中给出的示例)。

So you will override equals() in these situations and you would give your own conditions for equality.因此,在这些情况下,您将覆盖equals()并给出自己的平等条件。

I have successfully implemented equals() and it is working great.So why are they asking to override hashCode() as well?我已经成功实现了 equals() 并且效果很好。那么为什么他们还要求覆盖 hashCode() 呢?

Well.As long as you don't use "Hash" based Collections on your user-defined class,it is fine.好吧。只要您不在用户定义的类上使用基于“哈希”的集合,就可以了。 But some time in the future you might want to use HashMap or HashSet and if you don't override and "correctly implement" hashCode() , these Hash based collection won't work as intended.但是在未来的某个时候,您可能想要使用HashMapHashSet并且如果您不override“正确实现” hashCode() ,这些基于 Hash 的集合将无法按预期工作。

Override only equals (Addition to @Lombo 's answer)覆盖仅等于(除了@Lombo 的答案)

myMap.put(first,someValue)
myMap.contains(second); --> But it should be the same since the key are the same.But returns false!!! How?

First of all,HashMap checks if the hashCode of second is the same as first .首先,HashMap 检查second的 hashCode 是否与first相同。 Only if the values are the same,it will proceed to check the equality in the same bucket.只有当值相同时,它才会继续检查同一个桶中的相等性。

But here the hashCode is different for these 2 objects (because they have different memory address-from default implementation).但是这里这两个对象的 hashCode 是不同的(因为它们的内存地址与默认实现不同)。 Hence it will not even care to check for equality.因此它甚至不会关心检查是否相等。

If you have a breakpoint inside your overridden equals() method,it wouldn't step in if they have different hashCodes.如果您在重写的 equals() 方法中有一个断点,如果它们具有不同的 hashCode,它就不会介入。 contains() checks hashCode() and only if they are the same it would call your equals() method. contains()检查hashCode()并且只有当它们相同时才会调用您的equals()方法。

Why can't we make the HashMap check for equality in all the buckets?为什么我们不能让 HashMap 检查所有桶中的相等性? So there is no necessity for me to override hashCode() !!所以我没有必要覆盖 hashCode() !!

Then you are missing the point of Hash based Collections.那么你就错过了基于哈希的集合的重点。 Consider the following :考虑以下 :

Your hashCode() implementation : intObject%9.

The following are the keys stored in the form of buckets.以下是以桶的形式存储的密钥。

Bucket 1 : 1,10,19,... (in thousands)
Bucket 2 : 2,20,29...
Bucket 3 : 3,21,30,...
...

Say,you want to know if the map contains the key 10. Would you want to search all the buckets?假设,您想知道地图是否包含键 10。您想搜索所有桶吗? or Would you want to search only one bucket?或者您只想搜索一个存储桶?

Based on the hashCode,you would identify that if 10 is present,it must be present in Bucket 1. So only Bucket 1 will be searched !!根据 hashCode,您会发现如果 10 存在,则它必须存在于 Bucket 1 中。因此只会搜索 Bucket 1 !!

It is useful when using Value Objects .它在使用值对象时很有用。 The following is an excerpt from the Portland Pattern Repository :以下是Portland Pattern Repository的摘录:

Examples of value objects are things like numbers, dates, monies and strings.值对象的示例是数字、日期、货币和字符串等。 Usually, they are small objects which are used quite widely.通常,它们是使用相当广泛的小物体。 Their identity is based on their state rather than on their object identity.他们的身份基于他们的状态而不是他们的对象身份。 This way, you can have multiple copies of the same conceptual value object.这样,您可以拥有同一个概念值对象的多个副本。

So I can have multiple copies of an object that represents the date 16 Jan 1998. Any of these copies will be equal to each other.因此,我可以拥有代表 1998 年 1 月 16 日的对象的多个副本。这些副本中的任何一个都将彼此相等。 For a small object such as this, it is often easier to create new ones and move them around rather than rely on a single object to represent the date.对于像这样的小对象,通常更容易创建新对象并移动它们,而不是依赖单个对象来表示日期。

A value object should always override .equals() in Java (or = in Smalltalk).值对象应始终覆盖 Java 中的 .equals()(或 Smalltalk 中的 =)。 (Remember to override .hashCode() as well.) (记住也要覆盖 .hashCode() 。)

class A {
    int i;
    // Hashing Algorithm
    if even number return 0 else return 1
    // Equals Algorithm,
    if i = this.i return true else false
}
  • put('key','value') will calculate the hash value using hashCode() to determine the bucket and uses equals() method to find whether the value is already present in the Bucket. put('key','value') 将使用hashCode()计算哈希值以确定存储桶并使用equals()方法查找该值是否已存在于存储桶中。 If not it will added else it will be replaced with current value如果不是它会添加否则它将被替换为当前值
  • get('key') will use hashCode() to find the Entry (bucket) first and equals() to find the value in Entry get('key') 将首先使用hashCode()查找 Entry (bucket) 并使用equals()查找 Entry 中的值

if Both are overridden,如果两者都被覆盖,

Map< A >地图<A>

Map.Entry 1 --> 1,3,5,...
Map.Entry 2 --> 2,4,6,...

if equals is not overridden如果 equals 没有被覆盖

Map< A >地图<A>

Map.Entry 1 --> 1,3,5,...,1,3,5,... // Duplicate values as equals not overridden
Map.Entry 2 --> 2,4,6,...,2,4,..

If hashCode is not overridden如果 hashCode 未被覆盖

Map< A >地图<A>

Map.Entry 1 --> 1
Map.Entry 2 --> 2
Map.Entry 3 --> 3
Map.Entry 4 --> 1
Map.Entry 5 --> 2
Map.Entry 6 --> 3 // Same values are Stored in different hasCodes violates Contract 1
So on...

HashCode Equal Contract HashCode 等价合约

  1. Two keys equal according to equal method should generate same hashCode根据equal方法相等的两个键应该生成相同的hashCode
  2. Two Keys generating same hashCode need not be equal (In above example all even numbers generate same hash Code)生成相同 hashCode 的两个 Key 不必相等(在上面的例子中所有偶数生成相同的 hash Code)

1) The common mistake is shown in the example below. 1)常见错误如下例所示。

public class Car {

    private String color;

    public Car(String color) {
        this.color = color;
    }

    public boolean equals(Object obj) {
        if(obj==null) return false;
        if (!(obj instanceof Car))
            return false;   
        if (obj == this)
            return true;
        return this.color.equals(((Car) obj).color);
    }

    public static void main(String[] args) {
        Car a1 = new Car("green");
        Car a2 = new Car("red");

        //hashMap stores Car type and its quantity
        HashMap<Car, Integer> m = new HashMap<Car, Integer>();
        m.put(a1, 10);
        m.put(a2, 20);
        System.out.println(m.get(new Car("green")));
    }
}

the green Car is not found未找到绿色汽车

2. Problem caused by hashCode() 2. hashCode()引起的问题

The problem is caused by the un-overridden method hashCode() .该问题是由未覆盖的方法hashCode() The contract between equals() and hashCode() is: equals()hashCode()之间的契约是:

  1. If two objects are equal, then they must have the same hash code.如果两个对象相等,则它们必须具有相同的哈希码。
  2. If two objects have the same hash code, they may or may not be equal.如果两个对象具有相同的哈希码,则它们可能相等,也可能不相等。

     public int hashCode(){ return this.color.hashCode(); }

Assume you have class (A) that aggregates two other (B) (C), and you need to store instances of (A) inside hashtable.假设您有类 (A) 聚合了另外两个 (B) (C),并且您需要将 (A) 的实例存储在哈希表中。 Default implementation only allows distinguishing of instances, but not by (B) and (C).默认实现只允许区分实例,而不是通过 (B) 和 (C)。 So two instances of A could be equal, but default wouldn't allow you to compare them in correct way.所以 A 的两个实例可能相等,但默认情况下不允许您以正确的方式比较它们。

Consider collection of balls in a bucket all in black color.考虑在桶中收集所有黑色的球。 Your Job is to color those balls as follows and use it for appropriate game,你的工作是为这些球着色如下,并将其用于适当的游戏,

For Tennis - Yellow, Red.网球 - 黄色、红色。 For Cricket - White板球 - 白色

Now bucket has balls in three colors Yellow, Red and White.现在桶有黄、红、白三种颜色的球。 And that now you did the coloring Only you know which color is for which game.现在你做了着色只有你知道哪种颜色适合哪个游戏。

Coloring the balls - Hashing.给球上色 - 散列。 Choosing the ball for game - Equals.选择比赛用球 - 等于。

If you did the coloring and some one chooses the ball for either cricket or tennis they wont mind the color!!!如果您进行了着色并且有人为板球或网球选择了球,他们不会介意颜色!!!

I was looking into the explanation " If you only override hashCode then when you call myMap.put(first,someValue) it takes first, calculates its hashCode and stores it in a given bucket. Then when you call myMap.put(first,someOtherValue) it should replace first with second as per the Map Documentation because they are equal (according to our definition)."我正在研究解释“如果你只覆盖 hashCode 那么当你调用myMap.put(first,someValue)它首先计算它的 hashCode 并将其存储在给定的存储桶中。然后当你调用myMap.put(first,someOtherValue)它应该根据地图文档将第一个替换为第二个,因为它们是相等的(根据我们的定义)。” :

I think 2nd time when we are adding in myMap then it should be the 'second' object like myMap.put(second,someOtherValue)我认为第二次添加myMap它应该是“第二个”对象,如myMap.put(second,someOtherValue)

The methods equals and hashcode are defined in the object class.方法equals 和hashcode 在对象类中定义。 By default if the equals method returns true, then the system will go further and check the value of the hash code.默认情况下,如果 equals 方法返回 true,则系统将进一步检查哈希码的值。 If the hash code of the 2 objects is also same only then the objects will be considered as same.如果两个对象的哈希码也相同,则仅将对象视为相同。 So if you override only equals method, then even though the overridden equals method indicates 2 objects to be equal , the system defined hashcode may not indicate that the 2 objects are equal.因此,如果您只覆盖 equals 方法,那么即使覆盖的 equals 方法指示 2 个对象相等,系统定义的哈希码也可能不指示这 2 个对象相等。 So we need to override hash code as well.所以我们也需要覆盖哈希码。

Equals and Hashcode methods in Java Java 中的 Equals 和 Hashcode 方法

They are methods of java.lang.Object class which is the super class of all the classes (custom classes as well and others defined in java API).它们是 java.lang.Object 类的方法,它是所有类(自定义类以及在 java API 中定义的其他类)的超类。

Implementation:执行:

public boolean equals(Object obj)公共布尔等于(对象 obj)

public int hashCode()公共 int hashCode()

在此处输入图片说明

public boolean equals(Object obj)公共布尔等于(对象 obj)

This method simply checks if two object references x and y refer to the same object.此方法仅检查两个对象引用 x 和 y 是否引用同一个对象。 ie It checks if x == y.即它检查是否 x == y。

It is reflexive: for any reference value x, x.equals(x) should return true.它是自反的:对于任何参考值 x,x.equals(x) 应该返回 true。

It is symmetric: for any reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.它是对称的:对于任何参考值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 应该返回 true。

It is transitive: for any reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.它是可传递的:对于任何参考值 x、y 和 z,如果 x.equals(y) 返回 true 并且 y.equals(z) 返回 true,那么 x.equals(z) 应该返回 true。

It is consistent: for any reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the object is modified.它是一致的:对于任何引用值 x 和 y,x.equals(y) 的多次调用始终返回 true 或始终返回 false,前提是没有修改对象的 equals 比较中使用的信息。

For any non-null reference value x, x.equals(null) should return false.对于任何非空引用值 x,x.equals(null) 应返回 false。

public int hashCode()公共 int hashCode()

This method returns the hash code value for the object on which this method is invoked.此方法返回调用此方法的对象的哈希码值。 This method returns the hash code value as an integer and is supported for the benefit of hashing based collection classes such as Hashtable, HashMap, HashSet etc. This method must be overridden in every class that overrides the equals method.此方法以整数形式返回哈希码值,并且支持基于哈希的集合类,例如 Hashtable、HashMap、HashSet 等。必须在每个覆盖 equals 方法的类中覆盖此方法。

The general contract of hashCode is: hashCode 的总合约为:

Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified.每当在 Java 应用程序执行期间在同一对象上多次调用它时,hashCode 方法必须始终返回相同的整数,前提是对象上的 equals 比较中使用的信息没有被修改。

This integer need not remain consistent from one execution of an application to another execution of the same application.该整数不需要从应用程序的一次执行到同一应用程序的另一次执行保持一致。

If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.如果根据 equals(Object) 方法两个对象相等,则对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果。

It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results.如果根据 equals(java.lang.Object) 方法两个对象不相等,则不需要对两个对象中的每一个调用 hashCode 方法必须产生不同的整数结果。 However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.但是,程序员应该意识到为不相等的对象生成不同的整数结果可能会提高哈希表的性能。

Equal objects must produce the same hash code as long as they are equal, however unequal objects need not produce distinct hash codes.只要它们相等,相等的对象就必须产生相同的散列码,但不相等的对象不需要产生不同的散列码。

Resources:资源:

JavaRanch Java牧场

Picture 图片

If you override equals() and not hashcode() , you will not find any problem unless you or someone else uses that class type in a hashed collection like HashSet .如果您覆盖equals()而不是hashcode() ,则不会发现任何问题,除非您或其他人在HashSet等散列集合中使用该类类型。 People before me have clearly explained the documented theory multiple times, I am just here to provide a very simple example.在我之前的人已经多次清楚地解释了文档化的理论,我在这里只是提供一个非常简单的例子。

Consider a class whose equals() need to mean something customized :-考虑一个类,它的equals()需要表示一些定制的东西:-

    public class Rishav {

        private String rshv;

        public Rishav(String rshv) {
            this.rshv = rshv;
        }

        /**
        * @return the rshv
        */
        public String getRshv() {
            return rshv;
        }

        /**
        * @param rshv the rshv to set
        */
        public void setRshv(String rshv) {
            this.rshv = rshv;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof Rishav) {
                obj = (Rishav) obj;
                if (this.rshv.equals(((Rishav) obj).getRshv())) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        @Override
        public int hashCode() {
            return rshv.hashCode();
        }

    }

Now consider this main class :-现在考虑这个主类:-

    import java.util.HashSet;
    import java.util.Set;

    public class TestRishav {

        public static void main(String[] args) {
            Rishav rA = new Rishav("rishav");
            Rishav rB = new Rishav("rishav");
            System.out.println(rA.equals(rB));
            System.out.println("-----------------------------------");

            Set<Rishav> hashed = new HashSet<>();
            hashed.add(rA);
            System.out.println(hashed.contains(rB));
            System.out.println("-----------------------------------");

            hashed.add(rB);
            System.out.println(hashed.size());
        }

    }

This will yield the following output :-这将产生以下输出:-

    true
    -----------------------------------
    true
    -----------------------------------
    1

I am happy with the results.我对结果很满意。 But if I have not overridden hashCode() , it will cause nightmare as objects of Rishav with same member content will no longer be treated as unique as the hashCode will be different, as generated by default behavior, here's the would be output :-但是,如果我没有覆盖hashCode() ,它会导致噩梦,因为具有相同成员内容的Rishav对象将不再被视为唯一的,因为hashCode将不同,这是默认行为生成的,这是输出:-

    true
    -----------------------------------
    false
    -----------------------------------
    2

hashCode() method is used to get a unique integer for given object. hashCode()方法用于获取给定对象的唯一整数。 This integer is used for determining the bucket location, when this object needs to be stored in some HashTable , HashMap like data structure.这个整数用于确定桶的位置,当这个对象需要存储在一些HashTableHashMap类的数据结构中时。 By default, Object's hashCode() method returns and integer representation of memory address where object is stored.默认情况下,对象的hashCode()方法返回存储对象的内存地址的整数表示。

The hashCode() method of objects is used when we insert them into a HashTable , HashMap or HashSet .当我们将对象插入HashTableHashMapHashSet时,将使用对象的hashCode()方法。 More about HashTables on Wikipedia.org for reference.有关 Wikipedia.org 上的HashTables更多信息,以供参考。

To insert any entry in map data structure, we need both key and value.要在地图数据结构中插入任何条目,我们需要键和值。 If both key and values are user define data types, the hashCode() of the key will be determine where to store the object internally.如果键和值都是用户定义的数据类型,键的hashCode()将决定内部存储对象的位置。 When require to lookup the object from the map also, the hash code of the key will be determine where to search for the object.当还需要从地图中查找对象时,键的哈希码将决定在哪里搜索对象。

The hash code only points to a certain "area" (or list, bucket etc) internally.哈希码仅在内部指向某个“区域”(或列表、存储桶等)。 Since different key objects could potentially have the same hash code, the hash code itself is no guarantee that the right key is found.由于不同的密钥对象可能具有相同的哈希码,因此哈希码本身并不能保证找到正确的密钥。 The HashTable then iterates this area (all keys with the same hash code) and uses the key's equals() method to find the right key. HashTable然后迭代这个区域(所有键都具有相同的哈希码)并使用键的equals()方法来找到正确的键。 Once the right key is found, the object stored for that key is returned.一旦找到正确的键,就返回为该键存储的对象。

So, as we can see, a combination of the hashCode() and equals() methods are used when storing and when looking up objects in a HashTable .因此,如我们所见,在HashTable存储和查找对象时,使用了hashCode()equals()方法的组合。

NOTES:笔记:

  1. Always use same attributes of an object to generate hashCode() and equals() both.始终使用对象的相同属性来生成hashCode()equals()两者。 As in our case, we have used employee id.在我们的例子中,我们使用了员工 ID。

  2. equals() must be consistent (if the objects are not modified, then it must keep returning the same value). equals()必须一致(如果对象没有被修改,那么它必须保持返回相同的值)。

  3. Whenever a.equals(b) , then a.hashCode() must be same as b.hashCode() .每当a.equals(b) ,那么a.hashCode()必须与b.hashCode()相同。

  4. If you override one, then you should override the other.如果您覆盖一个,那么您应该覆盖另一个。

http://parameshk.blogspot.in/2014/10/examples-of-comparable-comporator.html http://parameshk.blogspot.in/2014/10/examples-of-comparable-comporator.html

In the example below, if you comment out the override for equals or hashcode in the Person class, this code will fail to look up Tom's order.在下面的示例中,如果您在 Person 类中注释掉对 equals 或 hashcode 的覆盖,此代码将无法查找 Tom 的订单。 Using the default implementation of hashcode can cause failures in hashtable lookups.使用哈希码的默认实现会导致哈希表查找失败。

What I have below is a simplified code that pulls up people's order by Person.我在下面是一个简化的代码,它按人拉出人们的订单。 Person is being used as a key in the hashtable. Person 被用作哈希表中的键。

public class Person {
    String name;
    int age;
    String socialSecurityNumber;

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

    @Override
    public boolean equals(Object p) {
        //Person is same if social security number is same

        if ((p instanceof Person) && this.socialSecurityNumber.equals(((Person) p).socialSecurityNumber)) {
            return true;
        } else {
            return false;
        }

    }

    @Override
    public int hashCode() {        //I am using a hashing function in String.java instead of writing my own.
        return socialSecurityNumber.hashCode();
    }
}


public class Order {
    String[]  items;

    public void insertOrder(String[]  items)
    {
        this.items=items;
    }

}



import java.util.Hashtable;

public class Main {

    public static void main(String[] args) {

       Person p1=new Person("Tom",32,"548-56-4412");
        Person p2=new Person("Jerry",60,"456-74-4125");
        Person p3=new Person("Sherry",38,"418-55-1235");

        Order order1=new Order();
        order1.insertOrder(new String[]{"mouse","car charger"});

        Order order2=new Order();
        order2.insertOrder(new String[]{"Multi vitamin"});

        Order order3=new Order();
        order3.insertOrder(new String[]{"handbag", "iPod"});

        Hashtable<Person,Order> hashtable=new Hashtable<Person,Order>();
        hashtable.put(p1,order1);
        hashtable.put(p2,order2);
        hashtable.put(p3,order3);

       //The line below will fail if Person class does not override hashCode()
       Order tomOrder= hashtable.get(new Person("Tom", 32, "548-56-4412"));
        for(String item:tomOrder.items)
        {
            System.out.println(item);
        }
    }
}

String class and wrapper classes have different implementation of equals() and hashCode() methods than Object class. String 类和包装类具有与 Object 类不同的equals()hashCode()方法实现。 equals() method of Object class compares the references of the objects, not the contents. Object 类的 equals() 方法比较对象的引用,而不是内容。 hashCode() method of Object class returns distinct hashcode for every single object whether the contents are same. Object 类的 hashCode() 方法为每个对象返回不同的哈希码,无论内容是否相同。

It leads problem when you use Map collection and the key is of Persistent type, StringBuffer/builder type.当您使用 Map 集合并且键是 Persistent 类型,StringBuffer/builder 类型时,它会导致问题。 Since they don't override equals() and hashCode() unlike String class, equals() will return false when you compare two different objects even though both have same contents.由于与 String 类不同,它们不会覆盖 equals() 和 hashCode(),因此当您比较两个不同的对象时,即使它们具有相同的内容,equals() 也会返回 false。 It will make the hashMap storing same content keys.它将使 hashMap 存储相同的内容键。 Storing same content keys means it is violating the rule of Map because Map doesnt allow duplicate keys at all.存储相同的内容键意味着它违反了 Map 规则,因为 Map 根本不允许重复键。 Therefore you override equals() as well as hashCode() methods in your class and provide the implementation(IDE can generate these methods) so that they work same as String's equals() and hashCode() and prevent same content keys.因此,您覆盖类中的 equals() 和 hashCode() 方法并提供实现(IDE 可以生成这些方法),以便它们与 String 的 equals() 和 hashCode() 工作相同并防止相同的内容键。

You have to override hashCode() method along with equals() because equals() work according hashcode.您必须将 hashCode() 方法与 equals() 一起覆盖,因为 equals() 根据哈希码工作。

Moreover overriding hashCode() method along with equals() helps to intact the equals()-hashCode() contract: "If two objects are equal, then they must have the same hash code."此外,覆盖 hashCode() 方法和 equals() 有助于完善 equals()-hashCode() 约定:“如果两个对象相等,则它们必须具有相同的哈希码。”

When do you need to write custom implementation for hashCode()?什么时候需要为 hashCode() 编写自定义实现?

As we know that internal working of HashMap is on principle of Hashing.众所周知,HashMap 的内部工作是基于 Hashing 的原理。 There are certain buckets where entrysets get stored.有一些存储条目集的存储桶。 You customize the hashCode() implementation according your requirement so that same category objects can be stored into same index.您可以根据自己的要求自定义 hashCode() 实现,以便可以将相同的类别对象存储到相同的索引中。 when you store the values into Map collection using put(k,v) method, the internal implementation of put() is:当您使用put(k,v)方法将值存储到 Map 集合中时, put(k,v)的内部实现是:

put(k, v){
hash(k);
index=hash & (n-1);
}

Means, it generates index and the index is generated based on the hashcode of particular key object.意思是,它生成索引,索引是基于特定键对象的哈希码生成的。 So make this method generate hashcode according your requirement because same hashcode entrysets will be stored into same bucket or index.因此,让此方法根据您的要求生成哈希码,因为相同的哈希码条目集将存储到相同的存储桶或索引中。

That's it!就是这样!

IMHO, it's as per the rule says - If two objects are equal then they should have same hash, ie, equal objects should produce equal hash values.恕我直言,这是按照规则说的 - 如果两个对象相等,那么它们应该具有相同的哈希值,即相等的对象应该产生相等的哈希值。

Given above, default equals() in Object is == which does comparison on the address, hashCode() returns the address in integer(hash on actual address) which is again distinct for distinct Object.上面给出,对象中的默认 equals() 是 == 对地址进行比较,hashCode() 以整数形式返回地址(实际地址上的散列),这对于不同的对象也是不同的。

If you need to use the custom Objects in the Hash based collections, you need to override both equals() and hashCode(), example If I want to maintain the HashSet of the Employee Objects, if I don't use stronger hashCode and equals I may endup overriding the two different Employee Objects, this happen when I use the age as the hashCode(), however I should be using the unique value which can be the Employee ID.如果需要在基于Hash的集合中使用自定义对象,则需要同时覆盖equals()和hashCode(),例如如果我想维护Employee对象的HashSet,如果我不使用更强的hashCode和equals我可能最终会覆盖两个不同的员工对象,当我使用年龄作为 hashCode() 时会发生这种情况,但是我应该使用可以是员工 ID 的唯一值。

To help you check for duplicate Objects, we need a custom equals and hashCode.为了帮助您检查重复的对象,我们需要一个自定义的 equals 和 hashCode。

Since hashcode always returns a number its always fast to retrieve an object using a number rather than an alphabetic key.由于哈希码总是返回一个数字,因此使用数字而不是字母键检索对象总是很快。 How will it do?它会怎么做? Assume we created a new object by passing some value which is already available in some other object.假设我们通过传递一些其他对象中已经可用的值来创建一个新对象。 Now the new object will return the same hash value as of another object because the value passed is same.现在新对象将返回与另一个对象相同的哈希值,因为传递的值相同。 Once the same hash value is returned, JVM will go to the same memory address every time and if in case there are more than one objects present for the same hash value it will use equals() method to identify the correct object.一旦返回相同的散列值,JVM 将每次都转到相同的内存地址,如果存在多个对象用于相同的散列值,它将使用 equals() 方法来识别正确的对象。

When you want to store and retrieve your custom object as a key in Map, then you should always override equals and hashCode in your custom Object .当您想在 Map 中存储和检索自定义对象作为键时,您应该始终覆盖自定义 Object 中的 equals 和 hashCode。 Eg:例如:

Person p1 = new Person("A",23);
Person p2 = new Person("A",23);
HashMap map = new HashMap();
map.put(p1,"value 1");
map.put(p2,"value 2");

Here p1 & p2 will consider as only one object and map size will be only 1 because they are equal.这里 p1 和 p2 将仅视为一个对象,并且map大小将仅为 1,因为它们相等。

public class Employee {

    private int empId;
    private String empName;

    public Employee(int empId, String empName) {
        super();
        this.empId = empId;
        this.empName = empName;
    }

    public int getEmpId() {
        return empId;
    }

    public void setEmpId(int empId) {
        this.empId = empId;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    @Override
    public String toString() {
        return "Employee [empId=" + empId + ", empName=" + empName + "]";
    }

    @Override
    public int hashCode() {
        return empId + empName.hashCode();
    }

    @Override
    public boolean equals(Object obj) {

        if (this == obj) {
            return true;
        }
        if (!(this instanceof Employee)) {
            return false;
        }
        Employee emp = (Employee) obj;
        return this.getEmpId() == emp.getEmpId() && this.getEmpName().equals(emp.getEmpName());
    }

}

Test Class测试班

public class Test {

    public static void main(String[] args) {
        Employee emp1 = new Employee(101,"Manash");
        Employee emp2 = new Employee(101,"Manash");
        Employee emp3 = new Employee(103,"Ranjan");
        System.out.println(emp1.hashCode());
        System.out.println(emp2.hashCode());
        System.out.println(emp1.equals(emp2));
        System.out.println(emp1.equals(emp3));
    }

}

In Object Class equals(Object obj) is used to compare address comparesion thats why when in Test class if you compare two objects then equals method giving false but when we override hashcode() the it can compare content and give proper result.在对象类中,equals(Object obj) 用于比较地址比较,这就是为什么在测试类中,如果您比较两个对象,则 equals 方法给出 false 但是当我们覆盖 hashcode() 时,它可以比较内容并给出正确的结果。

Both the methods are defined in Object class.这两种方法都在 Object 类中定义。 And both are in its simplest implementation.两者都是最简单的实现。 So when you need you want add some more implementation to these methods then you have override in your class.因此,当您需要为这些方法添加更多实现时,您可以在类中进行覆盖。

For Ex: equals() method in object only checks its equality on the reference.例如:对象中的 equals() 方法仅检查其在引用上的相等性。 So if you need compare its state as well then you can override that as it is done in String class.因此,如果您还需要比较它的状态,那么您可以像在 String 类中那样覆盖它。

There's no mention in this answer of testing the equals/hashcode contract.这个答案中没有提到测试 equals/hashcode 合同。

I've found the EqualsVerifier library to be very useful and comprehensive.我发现EqualsVerifier库非常有用和全面。 It is also very easy to use.它也很容易使用。

Also, building equals() and hashCode() methods from scratch involves a lot of boilerplate code.此外,从头开始构建equals()hashCode()方法涉及大量样板代码。 The Apache Commons Lang library provides the EqualsBuilder and HashCodeBuilder classes. Apache Commons Lang库提供了EqualsBuilderHashCodeBuilder类。 These classes greatly simplify implementing equals() and hashCode() methods for complex classes.这些类极大地简化了复杂类的equals()hashCode()方法的实现。

As an aside, it's worth considering overriding the toString() method to aid debugging.顺便说一句,值得考虑覆盖toString()方法以帮助调试。 Apache Commons Lang library provides the ToStringBuilder class to help with this. Apache Commons Lang库提供了ToStringBuilder类来帮助解决这个问题。

Recently I read through this Developer Works Document .最近,我通读了这份Developer Works文档

The document is all about defining hashCode() and equals() effectively and correctly, however I am not able to figure out why we need to override these two methods.该文档旨在有效,正确地定义hashCode()equals() ,但是我无法弄清为什么我们需要重写这两种方法。

How can I take the decision to implement these methods efficiently?我该如何决定有效地实施这些方法?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如果我覆盖Java中的'equals'方法,为什么需要重写hashcode? - Why is there a need to override hashcode if I override the 'equals' method in Java? 我需要实现hashCode()和equals()方法吗? - Do I need to implement hashCode() and equals() methods? 您是否需要为记录覆盖 hashCode() 和 equals()? - Do you need to override hashCode() and equals() for records? 如果我重写.equals(),即使我不使用HashMap,也需要重写.hashCode()吗? - If I override .equals(), do I need to override .hashCode() even if I don't use a HashMap? java中的equals()和hashcode()方法 - equals() and hashcode() methods in java 如果存在hashCode(),为什么Java需要equals()? - Why does Java need equals() if there is hashCode()? 为什么我们需要在java中重写equals和hashcode,为什么我们不能使用Object类实现 - why we need to override equals and hashcode in java and why cannot we use Object class implementation 哪个内置的Java类不会覆盖equals和hashcode方法? 为什么? - which inbuilt java class doesn't override equals and hashcode methods? why? Java集合:如果Hashmap的键是Immutable Object,那么我们是否需要重写哈希码和等于? - Java Collections: IF the key of Hashmap is Immutable Object then do we need to override hashcode and equals? 当我覆盖equals()方法时,为什么要覆盖hashCode()? - Why should I override hashCode() when I override equals() method?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM