Search code examples
javasetlanguage-concepts

Java: HashSet what is the Compare concept?


Coming from a c++ world, I find reading of the HashSet documentation somewhat hard:

In c++, you would have:

which in turns points to:

Which makes it obvious the requirement for the type of element handled by a std::set. My question is: What are the requirements for the type (E) of elements maintained by a Set in Java ?

Here is a short example which I fail to understand:

import gdcm.Tag;
import java.util.Set;
import java.util.HashSet;

public class TestTag
{
  public static void main(String[] args) throws Exception
    {
      Tag t1 = new Tag(0x8,0x8);
      Tag t2 = new Tag(0x8,0x8);
      if( t1 == t2 )
        throw new Exception("Instances are identical" );
      if( !t1.equals(t2) )
        throw new Exception("Instances are different" );
      if( t1.hashCode() != t2.hashCode() )
        throw new Exception("hashCodes are different" );
      Set<Tag> s = new HashSet<Tag>();
      s.add(t1);
      s.add(t2);
      if( s.size() != 1 )
        throw new Exception("Invalid size: " + s.size() );
    }
}

The above simple code fails with:

Exception in thread "main" java.lang.Exception: Invalid size: 2 at TestTag.main(TestTag.java:42)

From my reading of the documentation only the equals operator needs to be implemented for Set:

What am I missing from the documentation ?


Solution

  • I just tried to reproduce your issue, and maybe you just didn't override equals and/or hashSet correctly.

    Take a look at my incorrect implemenation of Tag:

    public class Tag {
    
    private int x, y;
    
    public Tag(int x, int y) {
        this.x = x;
        this.y = y;
    }
    
    public boolean equals(Tag tag) {
        if (x != tag.x) return false;
        return y == tag.y;
    }
    
    @Override
    public int hashCode() {
        int result = x;
        result = 31 * result + y;
        return result;
    }
    }
    

    Looks quite ok doesn't it? But the problem is, I actually do not override the correct equals method, I overloaded it with my own implementation.

    To work correctly, equals has to look like this:

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
    
        Tag tag = (Tag) o;
    
        if (x != tag.x) return false;
        return y == tag.y;
    }