如果hashcode返回一个常量值,则在HashSet.contains()的情况下调用hashCode()和equals()的次数

时间:2021-02-09 16:48:52

I have read through Java docs pages, but I am not able to explain, why number of calls of hashCode() and equals() is varying like this?

我已阅读过Java文档页面,但我无法解释为什么hashCode()和equals()的调用数量会像这样变化?

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class NumberOfCalls {
    int field;

    public int getField() {
        return field;
    }

    public NumberOfCalls(int field) {
        this.field = field;
    }

    @Override
    public int hashCode() {
        System.out.println("In Hashcode method.");
        return 10;
    }

    @Override
    public boolean equals(Object obj) {
        System.out.println("In Equals Method");
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        NumberOfCalls other = (NumberOfCalls) obj;
        if (field != other.field)
            return false;
        return true;
    }

    public static void main(String[] args) {
        NumberOfCalls object1 = new NumberOfCalls(5);
        NumberOfCalls object2 = new NumberOfCalls(6);
        NumberOfCalls object3 = new NumberOfCalls(5);
        Set<NumberOfCalls> set = new HashSet<NumberOfCalls>();
        set.add(object1);
        set.add(object2);
        Iterator<NumberOfCalls> it = set.iterator();
        System.out.print("Size of set is : " + set.size()
            + "\nObject fields values present in set are : ");
        while (it.hasNext()) {
            System.out.print(it.next().getField() + "   ");
        }
        System.out.println("\n---------------------------------------------------");
        System.out.println("Now checking number of calls -- ");
        System.out.println("Object1 is present in set ? - " + set.contains(object1)+"\n");
        System.out.println("Object2 is present in set ? - " + set.contains(object2)+"\n");
        System.out.println("Object3 is present in set ? - " + set.contains(object3)+"\n");
    }
}

Output for above code is

上面代码的输出是

In Hashcode method.
In Hashcode method.
In Equals Method
Size of set is : 2
Object fields values present in set are : 6   5

在Hashcode方法中。在Hashcode方法中。在等于方法中,set的大小为:2集合中存在的对象字段值为:6 5

---------------------------------------------------
Now checking number of calls --
In Hashcode method.
In Equals Method
Object1 is present in set ? - true

-------------------------------------------------- - 现在检查呼叫次数 - 在Hashcode方法中。在等于方法中,对象1存在于集合中? - 是的

In Hashcode method.
Object2 is present in set ? - true

在Hashcode方法中。对象2出现在集合中? - 是的

In Hashcode method.
In Equals Method
In Equals Method
Object3 is present in set ? - true

在Hashcode方法中。在等于方法中等于方法对象3存在于集合中? - 是的

Questions:

  1. Why hashCode() and equals() called one-one time in case of object1 but not object2 (only hashCode() is called in this case)?
  2. 为什么hashCode()和equals()在object1但不是object2的情况下调用一次(在这种情况下只调用hashCode())?

  3. Why equals() is called twice in case of object3?
  4. 为什么在object3的情况下调用equals()两次?

1 个解决方案

#1


When you add an element to a Set it gets stored in a Map internally where key is the object you pass in and value is set to null. Internally Map maintains an array (buckets) of linked list. These arrays can also be referred as buckets. The index of the bucket is evaluated using hashCode() method.

当您向Set添加元素时,它将存储在Map内部,其中key是您传入的对象,value设置为null。内部映射维护链表的数组(存储桶)。这些阵列也可称为存储桶。使用hashCode()方法评估存储桶的索引。

In your case since hashCode() returns a constant value, all values put into Set will go in the same bucket. So for the first call set.add(object1) internal structure will be something like

在您的情况下,因为hashCode()返回一个常量值,所以放入Set的所有值都将放在同一个桶中。所以对于第一次调用set.add(object1)的内部结构就是这样的

bucket [object1 -> null]

bucket [object1 - > null]

Since each bucket contains a linked list, elements stored in the list has a pointer to the next element. Since only one element is added to the list, pointer points to null.

由于每个存储桶包含一个链表,因此存储在列表中的元素具有指向下一个元素的指针。由于只有一个元素添加到列表中,因此指针指向null。

For next call set.add(object2) data structure will look like

对于下一次调用set.add(object2),数据结构将如下所示

bucket [object2 -> object1 -> null]

bucket [object2 - > object1 - > null]

Now whenever you call set.contains(some_object), hashCode() will be called to find out correct bucket. This call also returns reference to the first element.

现在每当你调用set.contains(some_object)时,都会调用hashCode()来找出正确的存储桶。此调用还返回对第一个元素的引用。

Answer to first question:

回答第一个问题:

So when you call set.contains(object2) it actually returns reference to object2. Now if you look at the HashMap class' code it first compares if this reference is same as the reference passed in, using == operator. Since in this case it is same so it does not call equals() method. Below is the code snippet from HashMap class:

因此,当您调用set.contains(object2)时,它实际上返回对object2的引用。现在,如果查看HashMap类的代码,它首先使用==运算符比较此引用是否与传入的引用相同。因为在这种情况下它是相同的所以它不会调用equals()方法。以下是HashMap类的代码片段:

if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;

This should explain your first question.

这应该解释你的第一个问题。

Answer to second question:

回答第二个问题:

When you call set.contains(object3), bucket index is calculated and reference to object2 is returned. Now object2 == object3 returns false so Map calls object3.equals(object2) method (first call to equals() method) to check if both objects are meaningfully equivalent. This also returns false, so it traverses list and returns reference to the next element in the list, which is object1.

当您调用set.contains(object3)时,将计算存储区索引并返回对object2的引用。现在object2 == object3返回false,因此Map调用object3.equals(object2)方法(首先调用equals()方法)来检查两个对象是否有意义相等。这也返回false,因此它遍历list并返回对列表中下一个元素的引用,即object1。

Again object1 == object3 returns false so Map calls object3.equals(object1) (second call to equals() method) method to check if both objects are meaningfully equivalent.

同样,object1 == object3返回false,因此Map调用object3.equals(object1)(第二次调用equals()方法)方法来检查两个对象是否有意义相等。

This results in 2 calls to equals() method.

这导致2次调用equals()方法。

#1


When you add an element to a Set it gets stored in a Map internally where key is the object you pass in and value is set to null. Internally Map maintains an array (buckets) of linked list. These arrays can also be referred as buckets. The index of the bucket is evaluated using hashCode() method.

当您向Set添加元素时,它将存储在Map内部,其中key是您传入的对象,value设置为null。内部映射维护链表的数组(存储桶)。这些阵列也可称为存储桶。使用hashCode()方法评估存储桶的索引。

In your case since hashCode() returns a constant value, all values put into Set will go in the same bucket. So for the first call set.add(object1) internal structure will be something like

在您的情况下,因为hashCode()返回一个常量值,所以放入Set的所有值都将放在同一个桶中。所以对于第一次调用set.add(object1)的内部结构就是这样的

bucket [object1 -> null]

bucket [object1 - > null]

Since each bucket contains a linked list, elements stored in the list has a pointer to the next element. Since only one element is added to the list, pointer points to null.

由于每个存储桶包含一个链表,因此存储在列表中的元素具有指向下一个元素的指针。由于只有一个元素添加到列表中,因此指针指向null。

For next call set.add(object2) data structure will look like

对于下一次调用set.add(object2),数据结构将如下所示

bucket [object2 -> object1 -> null]

bucket [object2 - > object1 - > null]

Now whenever you call set.contains(some_object), hashCode() will be called to find out correct bucket. This call also returns reference to the first element.

现在每当你调用set.contains(some_object)时,都会调用hashCode()来找出正确的存储桶。此调用还返回对第一个元素的引用。

Answer to first question:

回答第一个问题:

So when you call set.contains(object2) it actually returns reference to object2. Now if you look at the HashMap class' code it first compares if this reference is same as the reference passed in, using == operator. Since in this case it is same so it does not call equals() method. Below is the code snippet from HashMap class:

因此,当您调用set.contains(object2)时,它实际上返回对object2的引用。现在,如果查看HashMap类的代码,它首先使用==运算符比较此引用是否与传入的引用相同。因为在这种情况下它是相同的所以它不会调用equals()方法。以下是HashMap类的代码片段:

if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;

This should explain your first question.

这应该解释你的第一个问题。

Answer to second question:

回答第二个问题:

When you call set.contains(object3), bucket index is calculated and reference to object2 is returned. Now object2 == object3 returns false so Map calls object3.equals(object2) method (first call to equals() method) to check if both objects are meaningfully equivalent. This also returns false, so it traverses list and returns reference to the next element in the list, which is object1.

当您调用set.contains(object3)时,将计算存储区索引并返回对object2的引用。现在object2 == object3返回false,因此Map调用object3.equals(object2)方法(首先调用equals()方法)来检查两个对象是否有意义相等。这也返回false,因此它遍历list并返回对列表中下一个元素的引用,即object1。

Again object1 == object3 returns false so Map calls object3.equals(object1) (second call to equals() method) method to check if both objects are meaningfully equivalent.

同样,object1 == object3返回false,因此Map调用object3.equals(object1)(第二次调用equals()方法)方法来检查两个对象是否有意义相等。

This results in 2 calls to equals() method.

这导致2次调用equals()方法。