Java hashCode实现策略

Java hashCode实现策略,java,hashcode,Java,Hashcode,一些主要的JVM类(如or)通过为与equals方法相关的每个字段返回∑31^n*field\u n.hashCode()来实现equals。此外,Joshua Bloch在《有效Java》(项目9)中推荐了这种方法 但是,其他类(如)遵循不同的规则。例如,Map.Entry文档说明Map.Entry的哈希代码应该是 (e.getKey()==null ? 0 : e.getKey().hashCode()) ^ (e.getValue()==null ? 0 : e.getValue(

一些主要的JVM类(如or)通过为与
equals
方法相关的每个
字段返回
∑31^n*field\u n.hashCode()
来实现equals。此外,Joshua Bloch在《有效Java》(项目9)中推荐了这种方法

但是,其他类(如)遵循不同的规则。例如,Map.Entry文档说明
Map.Entry
的哈希代码应该是

 (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
 (e.getValue()==null ? 0 : e.getValue().hashCode())
这有时在哈希表中使用是不切实际的,因为:

  • 具有相同键和值的所有项的哈希代码为0
  • 两个条目e1和e2,以便e1.key=e2.value和e1.value=e2.key具有相同的哈希代码
为什么Java为
Map.Entry
hashCode选择此实现规范,而不是例如
31*(e.getKey()==null?0:e.getKey().hashCode())+(e.getValue()==null?0:e.getValue().hashCode())

编辑1:

为了帮助解决这个问题,下面是一个有用代码的示例,如果许多条目具有相同的键和值,那么由于哈希冲突,结果的性能非常差

该方法计算不同地图条目的频率(使用Guava的Multiset)

公共静态多集计算计数(
(可编辑地图){
ImmutableMultiset.Builder结果=ImmutableMultiset.Builder();
用于(地图:地图){
对于(Map.Entry:Map.entrySet()){
结果。添加(条目);
}
}
返回result.build();
}

我个人的猜测是,hashCode也应该很快

因为您可以重写哈希代码,所以它没有问题。
当你知道一个更适合你的案例的算法时,就改变它。我怀疑有什么好的理由——我认为这只是一个疏忽——但这不是一个严重的问题。只有当您有一个
HashSet
HashMap
时才会出现这个问题,这是不常见的。(编辑为添加:或者,正如Joachim Sauer在下面指出的那样,
HashSet
HashMap
——也不常用。)


请注意,
HashMap
不使用
Map.Entry.hashCode()
,因为它只通过项的键查找项,所以它只使用
K.hashCode()

您的实现不会影响键和值为null的输出。另外,在Java中,一个Map中只有一个带有一个键的条目,一个键在任何Map实现中只出现一次。如果要计算多个映射中每个键值项的总计数,可能会发生这种情况。我看不出这会对这种情况产生什么影响,除非您要将所有映射中的所有映射项放置在一个映射中进行计数,但这显然是错误的。虽然
hashCode
映射项的定义
Map.Entry
(从这一点出发,
hashCode
映射项的定义
Map
)确实存在很大问题,但很少能找到
映射项或
映射项。Entry
被用作另一个
Map
中的键(至少在结构良好的代码中)抱歉,但这是不对的。
Map.Entry
是一个接口。它不是描述它实现的默认算法,而是子类可以覆盖的算法;相反,它规定了实现者必须实现的特定算法。当sun/oracle在所有情况下都需要某个实现时,它们必须使用抽象cla由于
Map.hashCode()
是根据其
Map.Entry
元素的
hashCode
定义的,因此这也会出现在
Map
集合中。但这些也可以很容易地避免(并且通常出于可读性和设计的原因也应该避免)。遇到这个问题所需要的只是
someHashSet.addAll(someMap.entrySet())
或类似的东西。所有像“a”=>“a”这样的条目都保证会发生冲突,性能保证会很差(或至少比JDK 8以来的最佳性能差)。您很少遇到它,但当您这样做时,它会非常糟糕。
public static <K, V> Multiset<Map.Entry<K, V>> computeEntryCounts(
        Iterable<Map<K, V>> maps) {
    ImmutableMultiset.Builder<Map.Entry<K, V>> result = ImmutableMultiset.builder();
    for (Map<K, V> map : maps) {
        for (Map.Entry<K, V> entry : map.entrySet()) {
            result.add(entry);
        }
    }
    return result.build();
}