为什么在添加HashSet和hashCode匹配时没有调用equals()?

san*_*n9w 31 java

当我运行此代码时,为什么只有hashCode()被调用not equalsmethod而我的hashCode()实现hashCode为两个条目生成相同的HashSet

import java.util.HashSet;

public class Test1 {
    public static void main(String[] args) {
        Student st=new Student(89);
        HashSet st1=new HashSet();
        st1.add(st);
        st1.add(st);
        System.out.println("Ho size="+st1.size());
    }
}
class Student{
    private int name;
    private int ID;
    public Student(int iD) {
        super();
        this.ID = iD;
    }
    @Override
    public int hashCode() {
        System.out.println("Hello-hashcode");
        return ID;
    }
    @Override
    public boolean equals(Object obj) {
        System.out.println("Hello-equals");
        if(obj instanceof Student){
            if(this.ID==((Student)obj).ID){
                return true;
            }
            else{
                return false;
            }
        }
        return false;  
    }
}
Run Code Online (Sandbox Code Playgroud)

这个输出是:

Hello-hashcode
Hello-hashcode
Ho size=1
Run Code Online (Sandbox Code Playgroud)

tha*_*guy 25

哈希集首先检查引用相等性,如果超过,则跳过该.equals调用.这是一个优化和工作,因为合同equals规定,如果a == b那样a.equals(b).

我附上了下面的源代码,突出显示了此检查.

如果您改为添加两个不同参考的相等元素,您将获得预期的效果:

    HashSet st1=new HashSet();
    st1.add(new Student(89));
    st1.add(new Student(89));
    System.out.println("Ho size="+st1.size());
Run Code Online (Sandbox Code Playgroud)

结果是

$ java Test1
Hello-hashcode
Hello-hashcode
Hello-equals
Ho size=1
Run Code Online (Sandbox Code Playgroud)

这是来自OpenJDK 7的源代码,指示了相等优化(来自HashMap,HashSet的底层实现):

public V put(K key, V value) {
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key.hashCode());
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
//                                         v-- HERE
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }

    modCount++;
    addEntry(hash, key, value, i);
    return null;
}
Run Code Online (Sandbox Code Playgroud)


rge*_*man 9

A HashSet使用a HashMap作为集合的支持机制.通常情况下,我们会期望hashCode并且equals会被调用以确保没有重复项.但是,该put方法(调用private putVal实际操作的方法)在源代码中进行优化:

if (e.hash == hash &&
    ((k = e.key) == key || (key != null && key.equals(k))))
Run Code Online (Sandbox Code Playgroud)

如果哈希码匹配,它首先检查在调用之前键是否相同equals.您正在传递相同的Student对象,因此它们已经存在==,因此||操作员会短路,并且equals永远不会被调用.

如果你传入一个不同的Student对象,但是相同ID,那么==就会返回false并被equals调用.