hashCode方法的最佳实现
我们如何确定集合的" hashCode()"方法的最佳实现(假设equals方法已被正确覆盖)?
解决方案
对于一个简单的类,通常最容易基于由equals()实现检查的类字段来实现hashCode()。
public class Zam {
private String foo;
private String bar;
private String somethingElse;
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Zam otherObj = (Zam)obj;
if ((getFoo() == null && otherObj.getFoo() == null) || (getFoo() != null && getFoo().equals(otherObj.getFoo()))) {
if ((getBar() == null && otherObj. getBar() == null) || (getBar() != null && getBar().equals(otherObj. getBar()))) {
return true;
}
}
return false;
}
public int hashCode() {
return (getFoo() + getBar()).hashCode();
}
public String getFoo() {
return foo;
}
public String getBar() {
return bar;
}
}
最重要的是使hashCode()和equals()保持一致:如果equals()对两个对象返回true,则hashCode()应该返回相同的值。如果equals()返回false,则hashCode()应该返回不同的值。
@ about8:那里有一个非常严重的错误。
Zam obj1 = new Zam("foo", "bar", "baz");
Zam obj2 = new Zam("fo", "obar", "baz");
相同的哈希码
你可能想要类似的东西
public int hashCode() {
return (getFoo().hashCode() + getBar().hashCode()).toString().hashCode();
(这些天,我们能直接从Java中的int获取hashCode吗?我认为它会进行一些自动广播。如果是这种情况,请跳过toString,这很丑陋。)
首先,请确保正确实施了equals。从IBM DeveloperWorks文章中:
Symmetry: For two references, a and b, a.equals(b) if and only if b.equals(a) Reflexivity: For all non-null references, a.equals(a) Transitivity: If a.equals(b) and b.equals(c), then a.equals(c)
然后确保他们与hashCode的关系尊重联系人(来自同一篇文章):
Consistency with hashCode(): Two equal objects must have the same hashCode() value
最后,良好的哈希函数应努力实现理想的哈希函数。
简要说明一下完成其他更详细的答案(根据代码):
如果我考虑如何在Java中创建哈希表(尤其是jGuru FAQ条目)的问题,我认为可以判断哈希码的其他一些标准是:
- 同步(算法是否支持并发访问)?
- 故障安全迭代(算法是否检测到在迭代过程中发生更改的集合)
- 空值(哈希码是否支持集合中的空值)
正如我们特别要求的集合一样,我想添加一个其他答案还没有提到的方面:HashMap不会期望将其键添加到集合后更改其哈希码。会打败整个目标...
如果我正确理解了问题,则我们有一个自定义集合类(即从Collection接口扩展的新类),并且我们想实现hashCode()方法。
如果集合类扩展了AbstractList,那么我们不必担心它,已经存在equals()和hashCode()的实现,该实现通过迭代所有对象并将其hashCodes()加在一起而起作用。
public int hashCode() {
int hashCode = 1;
Iterator i = iterator();
while (i.hasNext()) {
Object obj = i.next();
hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode());
}
return hashCode;
}
现在,如果我们想要的是计算特定类的哈希码的最佳方法,那么我通常使用^(按位互斥或者)运算符来处理我在equals方法中使用的所有字段:
public int hashCode(){
return intMember ^ (stringField != null ? stringField.hashCode() : 0);
}
任何将哈希值均匀分布在可能范围内的哈希方法都是不错的实现。查看有效的Java(http://books.google.com.au/books?id=ZZOiqZQIbRMC&dq=effective+java&pg=PP1&ots=UZMZ2siN25&sig=kR0n73DHJOn-D77qGj0wOxAxiZw&hl=zh-CN&sa=X&oi=book_result&res =在那儿实现哈希码(我认为是项目9)。
最好的实现?这是一个很难的问题,因为它取决于使用模式。
在几乎所有情况下,Josh Bloch的有效Java条款8(第二版)中都提出了合理的良好实现。最好的办法是在那里查找,因为作者在那里解释了为什么这种方法很好。
简短版
- 创建一个" int结果"并分配一个非零值。
- 如果字段f是字节,char,short或者int,则计算(int)f。
- 如果字段f是'long':计算
(int)(f ^(f >>> 32)); - 如果字段f是浮点数,则计算:Float.floatToIntBits(f);
- 如果字段f是一个double值:计算Double.doubleToLongBits(f)并像每个long值一样处理返回值;
- 如果字段f是一个对象:使用
hashCode()方法的结果;如果f == null,则使用0;否则,使用0。 - 如果字段f是一个数组:将每个字段视为单独的元素,并以递归的方式计算哈希值,然后组合值,如下所述。
- 将哈希值
c与result相结合:
result = 37 * result + c
- 返回结果
对于大多数使用情况,这应该导致哈希值的正确分配。
Apache Commons Lang中有效Java的hashcode()和equals()逻辑有很好的实现。检出HashCodeBuilder和EqualsBuilder。
about8.blogspot.com,我们说过
if equals() returns true for two objects, then hashCode() should return the same value. If equals() returns false, then hashCode() should return different values
我不同意你的看法。如果两个对象具有相同的哈希码,则不必表示它们相等。
如果A等于B,则A.hashcode必须等于B.hascode
但
如果A.hashcode等于B.hascode,则并不意味着A必须等于B
我更喜欢使用Objects类的Google收藏夹库中的fromm Google实用程序方法,该方法可以帮助我保持代码的整洁。通常,"等于"和"哈希码"方法是由IDE的模板生成的,因此它们并不干净。
在Apache Commons EqualsBuilder和HashCodeBuilder上使用反射方法。
如果我们使用eclipse,则可以使用以下命令生成" equals()"和" hashCode()":
Source -> Generate hashCode() and equals().
使用此函数,我们可以确定要用于相等性和哈希码计算的字段,然后Eclipse生成相应的方法。
最好使用Eclipse提供的功能,该功能做得很好,我们可以投入精力和精力来开发业务逻辑。

