简体   繁体   中英

Number of calls of hashCode() and equals() in case of HashSet.contains() if hashcode returns a constant value

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?

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

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

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

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

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. Why equals() is called twice in case of object3 ?

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.

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

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 .

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

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.

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:

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 .

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.

This results in 2 calls to equals() method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM