在我最近的问题中,我被告知我需要覆盖我的equals
和hashcode
方法(以及其他事情)。因此,我花了一些时间阅读了一些文章,并试图提出一个适当的实现。
以下是我读过的一些文章:
- 维基百科的哈希码
- StackOverflow重写等号和hashcode 为什么hashcode使用乘数31
所有的文章都很好。由于这是我第一次尝试这样做,我只是想确保我没有犯一些简单(或愚蠢)的错误。
我将使用name
来指示我的Person
对象是否等同于另一个Person
对象。这样做的原因是,所有其他变量可以变化,但名称将始终是唯一的。
已更新以反映建议的更改
public class Person {
private String name;
private int p_number;
private String address;
//other variables
public Person(String a_name) {
name = a_name;
}
public String getName() {
return name;
}
//other getters and setters
@Override
public boolean equals(Object o) {
if(o == null)
return false;
if(o == this)
return true;
if(!(o instanceof Person))
return false;
Person p = (Person) o;
return name.equals(p.name));
}
@Override
public int hashCode() {
return name.hashCode();
}
}
我的问题如下:
- 我是否正确实现了这些方法?
- 由于
name
是唯一确定唯一性的变量,我需要检查hashcode
中的任何其他变量吗? - 我在StackOverflow上读到31被选为一个很好的素数,但是现在选择一个更大的素数更好吗?有人能证实或否认这种说法吗?(该声明在上面的第三个链接中提出)
如果我没有正确地实现这些方法,我如何改变/改进它们?
In equals()
:
if(name.equals(p.getName()))
return true;
缺少false
,您可以:
// Both are Person instances, no need to use the accessor here
return name.equals(p.name);
对于hashCode()
,仅return name.hashCode()
;
name可以为空吗?你的方法似乎不能解释这一点。(编辑:答案:no)
关于你的问题:
既然name是唯一决定唯一性的变量,我还需要检查hashcode中的其他变量吗?
不,当然不是!如果你的名字相同但年龄不同,这将导致相同的对象产生不同的哈希码,这违反了Object
合约!
我在StackOverflow上读到31被选为一个很好的素数,但是现在选择一个更大的素数更好吗?有人能证实或否认这种说法吗?(在上面的第三个链接中提出了索赔)
这个,不知道…
为了更完整地了解.equals()
/.hashCode()
合约,我将提到一个来自Guava的实用程序类:Equivalence
。类的抽象类的实现类可以允许您创建Set
s和Map
s,并将这些对象作为成员(键),就好像它们对这两个函数具有不同的实现:
Equivalence<MyClass> eq = ....;
Set<Equivalence.Wrapper<MyClass>> set = ...;
set.add(eq.wrap(myClassInstance));
在某些情况下,非常有用。
您的equals
需要在所有情况下返回一个值,只需更改结束部分以返回name.equals
。
@Override
public boolean equals(Object o) {
...
return name.equals(o.getName());
}
你的哈希码实际上是有害的,它所做的就是使用name.hashCode()
,但然后乘以31,最好直接使用名称中的默认Java字符串哈希码。
@Override
public int hashCode() {
return name.hashCode();
}