简体   繁体   中英

How contains method of Set collection works

Hi i am new to java as i know set collection doesn't take duplicate and its contains method should return true when element is already present in collection. I am trying to run below program but i am getting unexpected result.

public class UserDefinedName {
    private final String first, last;

    public UserDefinedName(String first, String last) {
        this.first = first;
        this.last = last;
    }

    public boolean equals(Object o) {
        if (!(o instanceof UserDefinedName))
            return false;
        UserDefinedName n = (UserDefinedName) o;
        return n.first.equals(first) && n.last.equals(last);
    }

    public static void main(String[] args) {
        Set<UserDefinedName> s = new HashSet<UserDefinedName>();
        s.add(new UserDefinedName("Carballo", "Videl"));
        System.out.println(s.contains(new UserDefinedName("Carballo", "Videl")));
    }
}

i am expecting output true but program prints false . what i am doing wrong?

form java doc

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.

If hashCode() method is not override then Object's hashCode() method is used which is default implementation.

in your case you have override equals method but you use default implementation of hashCode() as you didnt override hashCode() method in your UserDefinedName class.

The UserDefinedName class overrides the equals method, and the hashCode contract demands that equal objects have equal hash codes. To fulfill this contract, you must override hashCode whenever you override equals

add following code and it will work.

public int hashCode() {
return 37 * first.hashCode() + last.hashCode(); 
}

Because an hashSet uses the hashcode value of the object to store it. So it requires that you have to override the method hashCode in your own class. Since you didn't override it, it uses the hashCode method inherited from the object class.

The hashcode from the object class is calculated with the memory address of your object, ie :

UserDefinedName p = new UserDefinedName("Carballo", "Videl");
System.out.println(p);
System.out.println(0x1e5e2c3);
System.out.println(p.hashCode());

Output :

UserDefinedName@1e5e2c3
31843011
31843011

So if you tried this, you will see that it outputs true :

Set<UserDefinedName> s = new HashSet<UserDefinedName>();
UserDefinedName p = new UserDefinedName("Carballo", "Videl");
s.add(p);
System.out.println(s.contains(p));

Now if you want to compare your User class correclty, you will have to override your hashCode method (you can generated it with eclipse) to generate the hashCode your object field by field.

If you have this method to your class, this will print true for the code you provide.

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((first == null) ? 0 : first.hashCode());
        result = prime * result + ((last == null) ? 0 : last.hashCode());
        return result;
    }

When you are overriding equals method, then always override hashcode method also. A simple rule is, if two objects are considered to be equal then they should return the same hashcode.

I took your code and generated equals and hashcode with the help of eclipse

import java.util.HashSet;
import java.util.Set;

public class UserDefinedName {
private final String first, last;

public UserDefinedName(String first, String last) {
    this.first = first;
    this.last = last;
}

public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((first == null) ? 0 : first.hashCode());
    result = prime * result + ((last == null) ? 0 : last.hashCode());
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    UserDefinedName other = (UserDefinedName) obj;
    if (first == null) {
        if (other.first != null)
            return false;
    } else if (!first.equals(other.first))
        return false;
    if (last == null) {
        if (other.last != null)
            return false;
    } else if (!last.equals(other.last))
        return false;
    return true;
}

public static void main(String[] args) {
    Set<UserDefinedName> s = new HashSet<UserDefinedName>();
    s.add(new UserDefinedName("Carballo", "Videl"));
    System.out.println(s.contains(new UserDefinedName("Carballo", "Videl")));
    UserDefinedName obj1 = new UserDefinedName("prasad", "kharkar");
    UserDefinedName obj2 = new UserDefinedName("prasad", "kharkar");

    System.out.println(obj1.equals(obj2));
    System.out.println(obj1.hashCode());
    System.out.println(obj2.hashCode());

    }
}

the output is

true
true
-1072813416
-1072813416

Along with equals, you need to implement hashcode() method also.

HashSet is a data structure which uses HashMap internally. In a HashMap objects are stored in key value pairs. But, HashSet only accepts key, the corresponding value being an empty dummy object (This is ofcourse taken care by JVM). Now, for this hashing mechanism to work properly you need to implement equals() as well as hashCode() method. According to the JDK documentation :

This class implements the Set interface, backed by a hash table (actually a HashMap instance). It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time. This class permits the null element.

This class offers constant time performance for the basic operations (add, remove, contains and size), assuming the hash function disperses the elements properly among the buckets. Iterating over this set requires time proportional to the sum of the HashSet instance's size (the number of elements) plus the "capacity" of the backing HashMap instance (the number of buckets).

You might like to check "Effective Java - Second Edition - Chapter Three - Methods common to all objects". That will give you a clear understanding about the best practices of implementing equals() and hashCode() methods.

HashSet需要重写hashCodeequals方法。

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