hashCode方法的最佳实现

时间:2020-03-06 14:31:36  来源:igfitidea点击:

我们如何确定集合的" 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是一个数组:将每个字段视为单独的元素,并以递归的方式计算哈希值,然后组合值,如下所述。
  • 将哈希值cresult相结合:
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提供的功能,该功能做得很好,我们可以投入精力和精力来开发业务逻辑。