Java Hashset.contains() 产生神秘的结果
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4115978/
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
Java Hashset.contains() produces mysterious result
提问by Jochen
I don't usually code in Java, but recently I started not having a choice. I might have some major misunderstanding of how to properly use HashSet. So it might be possible something I did is just plain wrong. However I'm grateful for any help, you might offer. So the actual problem:
我通常不使用 Java 编写代码,但最近我开始别无选择。我可能对如何正确使用 HashSet 有一些重大误解。所以我所做的事情可能是完全错误的。但是,我很感激您提供的任何帮助。所以实际问题:
In a small program I was writing, I was generating very similar objects, which, when created, would have a very specific id (a string
or in my last iteration a long
). Because each object would spawn new objects, I wanted to filter out all those I already created. So I started throwing the id of every new object into my Hash(Set) and testing with HashSet.contains()
, if an object was created before. Here is the complete code:
在我编写的一个小程序中,我生成了非常相似的对象,这些对象在创建时将具有非常具体的 id(astring
或在我的上一次迭代中 a long
)。因为每个对象都会产生新的对象,所以我想过滤掉我已经创建的所有对象。因此,我开始将每个新对象的 id 放入我的 Hash(Set) 中,并使用HashSet.contains()
,进行测试,如果之前创建了一个对象。这是完整的代码:
// hashtest.java
import java.util.HashSet;
class L {
public long l;
public L(long l) {
this.l = l;
}
public int hashCode() {
return (int)this.l;
}
public boolean equals(L other) {
return (int)this.l == (int)other.l;
}
}
class hashtest {
public static void main(String args[]) {
HashSet<L> hash = new HashSet<L>();
L a = new L(2);
L b = new L(2);
hash.add(a);
System.out.println(hash.contains(a));
System.out.println(hash.contains(b));
System.out.println(a.equals(b));
System.out.println(a.hashCode() == b.hashCode());
}
}
produces following output:
产生以下输出:
true
false
true
true
so apparently, contains
does not use the equals
function provided by L
, or I have some major misunderstanding of the concept ...
很明显,contains
没有使用equals
提供的功能L
,或者我对这个概念有一些重大误解......
I tested it with openjdk (current version included in ubuntu) and the official current java from Oracle on Win7
我用 openjdk(包含在 ubuntu 中的当前版本)和来自 Oracle 在 Win7 上的官方当前 Java 对其进行了测试
for completeness official java-api documentation for HashSet.contains()
:
完整的官方 java-api 文档HashSet.contains()
:
public boolean contains(Object o)
Returns
true
if this set contains the specified element. More formally, returnstrue
if and only if this set contains an elemente
such that(o==null ? e==null : o.equals(e))
.
public boolean contains(Object o)
返回
true
此集合是否包含指定的元素。更正式地说,true
当且仅当此集合包含一个元素e
使得(o==null ? e==null : o.equals(e))
。
http://download.oracle.com/javase/6/docs/api/java/util/HashSet.html#contains(java.lang.Object)
http://download.oracle.com/javase/6/docs/api/java/util/HashSet.html#contains(java.lang.Object)
Any ideas or suggestions?
有什么想法或建议吗?
回答by SLaks
Your equals
method needs to take an Object
.
Because you declared it as taking an L
, it becomes an additional overload instead of overriding the method.
Therefore, when the hashSet
class calls equals
, it resolves to the base Object.equals
method. When you call equals
, you call your overload because a
and b
are both declaredas L
instead of Object
.
您的equals
方法需要采用Object
.
因为您将其声明为采用L
,所以它变成了一个额外的重载而不是覆盖该方法。
因此,当hashSet
类调用时equals
,它解析为基Object.equals
方法。当你调用时equals
,你调用了你的重载,因为a
和b
都被声明为L
而不是Object
。
To prevent this issue in the future, you should add @Override
whenever you override a method.
This way, the compiler will warn you if it isn't actually an override.
为防止将来出现此问题,您应该@Override
在覆盖方法时添加。
这样,如果它实际上不是覆盖,编译器会警告您。
回答by bcat
You're not actually overriding Object.equals
; instead, you're defining a new method with the same name but different parameters. Notice that Object.equals
takes an Object
argument, while your equals method takes an L
argument. If you rewrite your equals method to take an Object
and perform the necessary type-checking/casting to L
at runtime, then your code is work as you expect.
你实际上并没有覆盖Object.equals
;相反,您正在定义一个名称相同但参数不同的新方法。请注意,它Object.equals
接受一个Object
参数,而您的 equals 方法接受一个L
参数。如果您重写您的 equals 方法以获取 anObject
并L
在运行时执行必要的类型检查/强制转换,那么您的代码将按您的预期工作。
Also, this is why you really should use @Override
annotations whenever your JRE supports them. That way, the compiler will complain if you accidentally implement a new method when you intend to override an existing one.
此外,这就是为什么@Override
当您的 JRE 支持注释时,您确实应该使用注释。这样,如果您在打算覆盖现有方法时不小心实现了一个新方法,编译器就会抱怨。
By way of an example, this equals method should work correctly. (And, on an unrelated note, it won't fail if the object being compared to is null.)
举个例子,这个equals方法应该可以正常工作。(并且,在一个不相关的注释中,如果被比较的对象为空,它不会失败。)
@Override
public boolean equals(Object other) {
return other != null && other instanceof L && this.l == ((L)other).l;
}
回答by Jagadeesh
When you are adding objects to a set it internally calls equals
and hashCode
methods. You have to override these two methods. For example I have taken one bean class with name
,id
,designation
, then created and added an employee
object.
当您将对象添加到集合时,它会在内部调用equals
和hashCode
方法。您必须覆盖这两个方法。例如,我采用了一个带有name
, id
, 的bean 类designation
,然后创建并添加了一个employee
对象。
HashSet<Employee> set = new HashSet<Employee>();
Employee employee = new Employee();
employee.setId(1);
employee.setName("jagadeesh");
employee.setDesignation("programmer");
set.add(employee);
Employee employee2 = new Employee();
employee2.setId(1);
employee2.setName("jagadeesh");
employee2.setDesignation("programmer");
set.add(employee2);
set.add()
calls internally the equals
and hashCode
methods. So you have to override these two methods in your bean class.
set.add()
在内部调用equals
和hashCode
方法。所以你必须在你的 bean 类中覆盖这两个方法。
@Override
public int hashCode(){
StringBuffer buffer = new StringBuffer();
buffer.append(this.name);
buffer.append(this.id);
buffer.append(this.designation);
return buffer.toString().hashCode();
}
@Override
public boolean equals(Object object){
if (object == null) return false;
if (object == this) return true;
if (this.getClass() != object.getClass())return false;
Employee employee = (Employee)object;
if(this.hashCode()== employee.hashCode())return true;
return false;
}
Here we are overriding equals()
and hashCode()
. When you add an object to the HashSet
method it internally iterates all objects and calls the equals
method. Hence we overrid hashCode
, it compares every objects hashCode
with its current hashCode
and returns true if both are equal, else it returns false.
在这里,我们覆盖了equals()
and hashCode()
。当您向HashSet
方法添加对象时,它会在内部迭代所有对象并调用该equals
方法。因此,我们覆盖hashCode
, 它将每个对象hashCode
与其当前对象进行比较,hashCode
如果两者相等则返回真,否则返回假。