11

so i have a question on hashcode() and equals() method

Let's say I just write a very basic program overridng both the methodes

import java.util.*;

class Employee
{
   private String name;
   private int empid;


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


   public int getEmpid()
   {
       return empid;
   }


   public String getName()
   {
       return name;
   }


   public boolean equals(Object obj)
   {
       System.out.println("equals has just been called...");
       Employee e1=(Employee)obj;
       return ((name.equals(e1.name)) && (empid==e1.empid));
   }


   public int hashCode()
   {
       System.out.println("hashcode called...");
       return empid;
   }

}

Then, let's say I write another class to add and iterate the elements in HashSet

class Five
{
   public static void main(String args[])
   {
       HashSet hs1=new HashSet();
       hs1.add(new Employee("Alex",25));
       hs1.add(new Employee("Peter",25));
       hs1.add(new Employee("Martin",25));
       hs1.add(new Employee("Alex",25));


       Iterator itr=hs1.iterator();

       while(itr.hasNext())
       {
           Employee e=(Employee)itr.next();
           System.out.println(e.getEmpid()+"\t"+e.getName());
       }


    }

}

now the question is when i try to add Alex again with the same empid the equals() always called thee times

as there is no index n hashmap so in case if it first be checked with previously added Alex it will return true and should not be called for the other two elements (peter and martin) but equals always called 3 times

why..??

is objects within same bucket do also have the index..??

RNJ
  • 14,308
  • 16
  • 73
  • 125
sandiee
  • 153
  • 1
  • 8
  • I'd take a look at the HashSet source code. From memory it actually adds it to a map so I suspect indexOf and equals are used several times in that – RNJ Jul 29 '13 at 08:43
  • ankur Lathi- nopss my que is different – sandiee Jul 29 '13 at 08:50

4 Answers4

14

Equals is always called after the hashCode method in a java hashed collection while adding and removing elements. The reason being, if there is an element already at the specified bucket, then JVM checks whether it is the same element which it is trying to put. In case if the equals returns false then the element is added to the same bucket but at the end of list at the bucket. So now you just dont have a single element at the same bucket but a list of elements.

Now while retrieving the element, first hashCode will be called to reach the desired bucket and then the list will be scanned using the equals to fetch the desired element.

The ideal implemenation of hashCode will make sure the size of list at each bucket is 1. And hence the retrieval of elements is done using O(1) complexity. But if there are mulitple elements stored in the list at a bucket, then the retreival of element will be done by O(n) complexiy, where n is the size of the list.

Btw in case of HashSet there is no list created at the bucket, rather the object is simply replaced if hashcode and equals are same. The ist creation behavior is in hashmap.

Juned Ahsan
  • 63,914
  • 9
  • 87
  • 123
  • 2
    I think the hashCode-method call is followed by the equals-method call... – Puce Jul 29 '13 at 08:48
  • 1
    @Puce No, hashCode is always called first to get the bucket, and then equals is called to check things at that bucket. – Juned Ahsan Jul 29 '13 at 08:52
  • juned ahsan- thnx sir.. it took some time but now got it and the explanation of complexity is the point i want – sandiee Jul 29 '13 at 08:53
  • 2
    @JunedAhsan exactly. I'm not a native English speaker, but shouldn't it be then "hashCode followed by equals"? – Puce Jul 29 '13 at 08:56
  • Note that "`equals` is *always* called after ..." is not exactly true - if references match `equals` is skipped (see [Why `equals()` is not called](http://stackoverflow.com/questions/25331926/why-is-equals-not-called) question). – Alexei Levenkov Aug 15 '14 at 20:52
  • Looks like ***IF*** `equals()` is called it's called after `hashCode()`, but it's not guaranteed that it'll be called. http://stackoverflow.com/a/25332022/496289 – Kashyap Oct 08 '15 at 21:57
3

During insert the HashSet first calls the hashCode and looks in which bucket the new value belongs to. It sees that there are already three entries (all with the hashCode() 25).

So it then compares by using equals(). And because there are 3 entries it has to check all entries causing to call equals() 3 times.

Uwe Plonus
  • 9,235
  • 4
  • 36
  • 47
  • 1
    I originally thought he violated the `equals()` and `hashCode()` contract too, but how could it ever actually affect anything? If they have the same `empid`, they map to the same array slot, and then `equals` is used to see if the `names` match up. I can't see an issue... – Steve P. Jul 29 '13 at 08:52
  • 3
    The `equals` and `hashCode` methods look fine to me - the `equals` uses both `name` and `empid` fields while the `hashCode` uses just `empid`. This does not validate the contract that says 'equal objects should have the same hash code, but the same hash code does not mean the objects are equal'. – Nick Holt Jul 29 '13 at 08:54
  • Actually, I think it only has to compare entries as long as equals returns false, but can stop once equals returns true. So this probably means that the first entry ("Alex",25) is at the end of the list for some reason? – Puce Jul 29 '13 at 09:01
  • Changed answer as the contract fr `equals()` and `hashCode()` is fine so far. Didn't read exactly enough. Rest of the answer is still validin my eyes. – Uwe Plonus Jul 29 '13 at 09:08
3

A java.util.HashSet uses a java.util.HashMap as its storage. A java.util.HashMap uses a linked Entry object to represent the buckets in the map. If you follow through the source code you will get to the contructor of java.util.HashMap.Entry:

Entry(int h, K k, V v, Entry<K,V> n) 
{
  value = v;
  next = n;
  key = k;
  hash = h;
}

From this you can see that new items are added to the start of the bucket (the Entry n representing the first Entry of the bucket), so in your case the items in the bucket (there's only a single bucket because the hash code for each Employee is the same) will be in the order:

Martin -> Peter -> Alex

Hence when adding Alex a second time, each value is checked for equality before getting to Alex.

Nick Holt
  • 31,429
  • 4
  • 46
  • 56
0

Multiple objects having same hash are stored as LinkedList and new elements are added at HEAD. So in your case since all have the same hash, LinkedList is in following order:

Martin->Peter->Alex.

When you add another "Alex" the list is traversed from HEAD.

To test:

public boolean equals(Object obj)
   {
       Employee e1=(Employee)obj;
       System.out.println(this.name +  "'s equals has just been called against " + e1.name );
       return ((name.equals(e1.name)) && (empid==e1.empid));
   }
rocketboy
  • 8,990
  • 1
  • 31
  • 36