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提供的功能,该功能做得很好,我们可以投入精力和精力来开发业务逻辑。