简体   繁体   中英

HashSet says it doesn't contain an element although equals returns true

class Node{
    int x, y, value;
    Node(int x, int y, int v) {
        this.x = x;
        this.y = y;
        value = v;
    }

    @Override
    public boolean equals(Object o) {
        Node n = (Node)o;
        boolean result = this.x == n.x && this.y == n.y && this.value == n.value;
        return result;
    }
}

void test() {
    HashSet<Node> s = new HashSet<>();
    Node n2 = new Node(1, 1, 11);
    Node n1 = new Node(1, 1, 11);
    s.add(n1);
    System.out.println(s.contains(n2));
    System.out.println(n1.equals(n2));
}

returns:

false

true

Per https://docs.oracle.com/javase/8/docs/api/java/util/HashSet.html#contains-java.lang.Object- , HashSet uses equal to judge if it contains an element. So shouldn't the contains call return true here? What am I missing? Thanks.

See the javadoc for Object.equals() :

Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.

That is what is missing in your class! This here can give you some ideas how to hashCode() your class.

The 1st thing that comes to my eye is that you are overriding equals in an inappropriate way. You are not considering, comparing reference, null object or class itself.

something like this is missing in your equals method:

if (this == obj)
    return true;
if (obj == null)
    return false;
if (getClass() != obj.getClass())
    return false;

On the other hand, you have to implement correctly the contract for the Node class: ie you need to override equals AND hashCode too

I will explain you on WHY to override hashcode() , for this, basically you need to understand how HashSet works.

First thing is because you are NOT overriding the hashcode() , your Node class will get the default implementation from java.lang.Object class.

You can add a System.out.println and print the hashcodes of both of the Node objects in your equals() as shown below:

@Override
public boolean equals(Object o) {
    Node n = (Node)o;
    //Add System.out.println to check the hashcodes
    System.out.println(n.hashCode()+"::::::"+this.hashCode());

    boolean result = this.x == n.x && this.y == n.y && this.value == n.value;
    return result;
}

You can check the output by running the above method, you will see that the hashcodes are different for both objects ie, this proved that the two objects are equal but their hashcodes are different .

Now coming to the hashSet.contains(n2) , how HashSet works is that it stores the objects into different buckets based on their hashcodes . So your two Node objects will fall under two different buckets and contains() will return false .

So, to summarise, the rule is that the equal objects must have equal hash codes , so you always need to override hashcode() along with your equals() so that you will not get the contradictory results like the above equals() and contains()

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