-1

I've come to this phenomenon this morning, the equals method in Set does not check for value equality of the element while List does. This is not in accordance with the java doc.

Set<MyClass> s1 = new HashSet<>();
Set<MyClass> s2 = new HashSet<>();
Set<MyClass> s3 = new HashSet<>();
Set<MyClass> s4 = new HashSet<>();
List<MyClass> l1 = new ArrayList<>();
List<MyClass> l2 = new ArrayList<>();

MyClass o1 = new MyClass();
MyClass o2 = new MyClass();

// **this gives false, and does not call MyClass.equals().**
s1.add(o1);
s2.add(o2);
boolean setCompareWithDifferentObjects = s1.equals(s2);

// this gives true, and also does not call MyClass.equals().
s3.add(o1);
s4.add(o1);
boolean setCompareWithSaveObjects = s3.equals(s4);

// this give true, and MyClass.equals() is called.
l1.add(o1);
l2.add(o2);
boolean listCompare = l1.equals(l2)

I've done some research. According to this Java doc for Set, HashSet equals , HashSet containsAll, HashSet contains, it will use (o==null ? e==null : o.equals(e)) to check whether the elements are equal. So why is this happen? Can anyone give me some hint on this?

Thanks!

----------Answer to this question can be found here -----------

What issues should be considered when overriding equals and hashCode in Java?

I overrided equals() but not hashCode()...

btw, the same set comparison worked in groovy, even if hashCode() is not overriden.

Community
  • 1
  • 1
xing
  • 464
  • 9
  • 17
  • 3
    Have you implemented both equals and hashcode methods properly? –  Jan 06 '16 at 00:11
  • I've implemented MyClass.equals() but not hashCode(). But in the first comparison, MyClass.equals is not entered at all, even if == fails. – xing Jan 06 '16 at 00:27
  • 5
    Sigh... if you have not implemented `hashCode()` this explains everything you are seeing. `equals()` and `hashCode()` MUST be consistent with each other. Also, please do not retype code into the SO input text box. Always copy/paste so you don't introduce transcription errors (`l2.add(l2)`). One such obvious error makes your entire code sample suspect, and leads to unnecessary requests for clarification. – Jim Garrison Jan 06 '16 at 00:29

1 Answers1

8

HashSet includes a number of optimizations that can explain all of this: first, if two objects are put into different buckets by their hash codes, or if they have different hash codes at all, they may skip the equals call. This is allowed by the contract of Object.hashCode; if two objects have different hash codes then they are not allowed to .equals to each other.

For the other case, HashSet takes advantage of the contract of .equals that specifies that if two objects are == to each other, then they must be .equals to each other. Part of HashSet's implementation here checks if the elements are ==, and if they are, it skips calling .equals.

If every method implements its contract correctly, this cannot change the semantics; HashSet will always behave exactly as if .equals were being called.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • This is the same in python. I was looking for evidence though. Can you link any? – Reut Sharabani Jan 06 '16 at 00:11
  • 3
    Look at the source code of `HashSet`, or actually, `HashMap`. [Here](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/HashMap.java#HashMap.containsKey%28java.lang.Object%29) is the source for `HashMap.containsKey`, which as stated, only looks in the same bucket, verifies that the hashes are exactly equal, and then tests if the objects are `==` before calling `equals`. – Louis Wasserman Jan 06 '16 at 00:12
  • The real problem is the first check, which gives false even the two obejcts are equal by means of equals(). Which means == gives false and HashSet does not call equals. This is what I think does not conform with the contract. – xing Jan 06 '16 at 00:26
  • 4
    @xing, then your objects violate their own contract for `hashCode()`. `HashSet` cannot work if your hash codes are invalid. Any two objects that are `.equals` must have the same `hashCode()`. – Louis Wasserman Jan 06 '16 at 00:26
  • I'm curious about the reason for the downvote. I've already stated that according to the `Object` contract, if two objects have different hash codes they are not allowed to be `.equals`. – Louis Wasserman Jan 06 '16 at 00:34
  • Yes that explains my questions. Thanks a lot! – xing Jan 06 '16 at 01:20