我们如何决定集合的hashCode()方法的最佳实现(假设equals方法已被正确重写)?


当前回答

由于您特别要求集合,我想添加一个其他答案还没有提到的方面:HashMap不期望它们的键在添加到集合后改变它们的hashcode。会破坏整个目的…

其他回答

如果我正确理解你的问题,你有一个自定义的集合类(即一个从集合接口扩展的新类),你想实现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);
}

标准实现很弱,使用它会导致不必要的冲突。想象一个

class ListPair {
    List<Integer> first;
    List<Integer> second;

    ListPair(List<Integer> first, List<Integer> second) {
        this.first = first;
        this.second = second;
    }

    public int hashCode() {
        return Objects.hashCode(first, second);
    }

    ...
}

Now,

new ListPair(List.of(a), List.of(b, c))

and

new ListPair(List.of(b), List.of(a, c))

List的乘数具有相同的hashCode,即31*(a+b) + c。hashCode在这里被重用。显然,碰撞是不可避免的,但产生不必要的碰撞只是……不必要的。

There's nothing substantially smart about using 31. The multiplier must be odd in order to avoid losing information (any even multiplier loses at least the most significant bit, multiples of four lose two, etc.). Any odd multiplier is usable. Small multipliers may lead to faster computation (the JIT can use shifts and additions), but given that multiplication has latency of only three cycles on modern Intel/AMD, this hardly matters. Small multipliers also leads to more collision for small inputs, which may be a problem sometimes.

使用质数是没有意义的,因为质数在环Z/(2**32)中没有意义。

因此,我建议使用随机选择的大奇数(可以选择质数)。由于i86/amd64 cpu可以使用更短的指令来匹配一个有符号字节的操作数,因此对于像109这样的乘法器来说,速度优势很小。为了最小化冲突,可以使用类似0x58a54cf5的值。

在不同的地方使用不同的乘数是有帮助的,但可能不足以证明额外的工作是合理的。

最好使用Eclipse提供的功能,它做得非常好,您可以把精力和精力放在开发业务逻辑上。

由于您特别要求集合,我想添加一个其他答案还没有提到的方面:HashMap不期望它们的键在添加到集合后改变它们的hashcode。会破坏整个目的…

只是一个快速的注释,以完成其他更详细的答案(在代码方面):

如果我考虑如何在java中创建哈希表的问题,特别是jGuru FAQ条目,我相信可以判断哈希代码的其他标准是:

同步(算法是否支持并发访问)? 失败安全迭代(算法是否检测到迭代过程中发生变化的集合) 空值(哈希码是否支持集合中的空值)