Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HashSet contains problem with custom objects

My Custom class that will be contained by HashSet

public class Person {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "hashcode='" + this.hashCode() + '\'' +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;

        Person person = (Person) o;

        if (age != person.age) return false;
        if (!name.equals(person.name)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + age;
        return result;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

My HashSet test that fails

   public void hashSetTest() {
        Set<Person>  personSet = new HashSet<Person>();
        Person p1 = new Person("raghu", 12);
        Person p2 = new Person("rimmu", 21);

        personSet.add(p1);
        personSet.add(p2);


       p1.setName("raghus");
       p1.setAge(13);

       int i2 =p1.hashCode();
       System.out.println(personSet.size() + ": "+ p1.hashCode()+" : "+personSet.contains(p1)+ " : "+i2);
    }

Iam expecting personSet.contains(p1) to pass. Why is it returning false? Thanks sri

like image 398
sridhar Avatar asked Feb 24 '11 20:02

sridhar


People also ask

Can HashSet have objects?

A few important features of HashSet are:Objects that you insert in HashSet are not guaranteed to be inserted in the same order. Objects are inserted based on their hash code. NULL elements are allowed in HashSet. HashSet also implements Serializable and Cloneable interfaces.

How do you check if a HashSet contains an object?

The contains() method of Java HashSet class is used to check if this HashSet contains the specified element or not. It returns true if element is found otherwise, returns false.

Does HashSet have Contains method?

HashSet contains() Method in JavaHashSet. contains() method is used to check whether a specific element is present in the HashSet or not. So basically it is used to check if a Set contains any particular element. Parameters: The parameter element is of the type of HashSet.

How do you add a custom object to a HashSet in Java?

Create Employee object. Notice I have override equals() and hasCode() method through which I am making Employee object unique based on attributes – name and address . Create HashSet test class to test the uniqueness of objects added to the HashSet.


3 Answers

Because p1.hashCode() changes when you modify p1, so it can't be found at its original index in the hash table anymore. Never let a hash value depend on a mutable field.

(You're quite lucky that it fails during testing; it might just as well have succeeded, only to fail in production.)

like image 151
Fred Foo Avatar answered Sep 20 '22 05:09

Fred Foo


HashSet implements Set. The ApiDoc specifies:

Note: Great care must be exercised if mutable objects are used as set elements. The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set.

In your example this is the case, because changing name or age on p1 affects equal-comparison. So according the ApiDoc, the behavior of Set in your case is unspecified.

like image 32
Michael Konietzka Avatar answered Sep 22 '22 05:09

Michael Konietzka


I think you need to have hashCode depends on mutable fields quite often indeed: when you override equals that depends on mutable fields.

From hashCode's contract: "If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result."

So, if you create two objects such that A.equals(B) is true, and then modify A such a way that you get A.equals(B) became false, you need to have hashCodes change too.

It's true that in hashCode's documentation is stated that "It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results.", but I don't know how this can help.

like image 26
think01 Avatar answered Sep 19 '22 05:09

think01