java 为什么在添加到 HashSet 和 hashCode 匹配时不调用 equals()?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25331926/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why is equals() not called while adding to HashSet and hashCode matches?
提问by san9w
When I run this code why only hashCode()
is called not equals
method while my hashCode()
implementation generate same hashCode
for both entries to HashSet
?
当我运行此代码时,为什么只hashCode()
调用 notequals
方法,而我的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;
}
}
The output for this is:
这个的输出是:
Hello-hashcode
Hello-hashcode
Ho size=1
采纳答案by that other guy
The hash set checks reference equality first, and if that passes, it skips the .equals
call. This is an optimization and works because the contract of equals
specifies that if a == b
then a.equals(b)
.
哈希集首先检查引用相等性,如果通过,则跳过.equals
调用。这是一种优化并且有效,因为合同equals
指定 if a == b
then a.equals(b)
。
I attached the source code below, with this check highlighted.
我附上了下面的源代码,并突出显示了此检查。
If you instead add two equal elements that are not the same reference, you get the effect you were expecting:
如果您改为添加两个不同引用的相等元素,则会得到您期望的效果:
HashSet st1=new HashSet();
st1.add(new Student(89));
st1.add(new Student(89));
System.out.println("Ho size="+st1.size());
results in
结果是
$ java Test1
Hello-hashcode
Hello-hashcode
Hello-equals
Ho size=1
Here's the source code from OpenJDK 7, with equality optimization indicated (from HashMap, the underlying implementation of HashSet):
这是来自 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;
}
回答by rgettman
A HashSet
uses a HashMap
as its backing mechanism for the set. Normally, we would expect that hashCode
and equals
would be called to ensure that there are no duplicates. However, the put
method (which calls a private
putVal
method to do the actual operation) makes an optimization in the source code:
AHashSet
使用 aHashMap
作为集合的支持机制。通常,我们会期望hashCode
并且equals
会被调用以确保没有重复。但是put
方法(调用一个private
putVal
方法来做实际操作)在源码中做了优化:
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
If the hash codes match, it first checks to see if the keys are the same before calling equals
. You are passing the same Student
object, so they are already ==
, so the ||
operator short-circuits, and equals
is never called.
如果哈希码匹配,它首先检查键是否相同,然后再调用equals
. 您正在传递相同的Student
对象,因此它们已经是==
,因此||
运算符短路,并且equals
永远不会被调用。
If you passed in a different Student
object but with the same ID
, then ==
would return false
and equals
would be called.
如果您传入不同的Student
对象但具有相同的ID
,==
则将返回false
并被equals
调用。
回答by SamYonnou
Looking through the source code of HashSet
it is using HashMap
for all of its operations and the add method performs put(element, SOME_CONSTANT_OBJECT)
. Here is the source code for the put method for JDK 1.6.0_17 :
查看HashSet
它HashMap
的所有操作的源代码,并且 add 方法执行put(element, SOME_CONSTANT_OBJECT)
。这是 JDK 1.6.0_17 的 put 方法的源代码:
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;
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;
}
as you can see it performs a ==
comparison before using the equals method. Since you are adding the same instance of an object twice the ==
returns true and the equals method is never called.
如您所见,它==
在使用 equals 方法之前执行比较。由于您添加了相同的对象实例两次,因此==
返回 true 并且永远不会调用 equals 方法。
回答by Abhi
Equals is always called after the hashCode method in a java hashed collection while adding and removing elements. The reason being, if there is an element already at the specified bucket, then JVM checks whether it is the same element which it is trying to put.
添加和删除元素时,始终在 java 散列集合中的 hashCode 方法之后调用 Equals。原因是,如果指定的存储桶中已经有一个元素,则 JVM 会检查它是否与它尝试放入的元素相同。
回答by Himanshu Sharma
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.
如果根据 equals(Object) 方法两个对象相等,则对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果。