当我运行此代码时,为什么只有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)
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调用.