【发布时间】:2012-04-16 02:23:02
【问题描述】:
一些主要的 JVM 类(例如 String 或 List implementations)通过为与 equals 方法相关的每个 field_n 返回 Σ 31^n * field_n.hashCode() 来实现 equals。此外,Joshua Bloch 在 Effective Java(第 9 项)中推荐了这种方法。
但是,Map.Entry implementations 等其他类遵循不同的规则。例如,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)。
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();
}
【问题讨论】:
-
您的实现不会影响键和值为 null 的输出。此外,在 Java 中的 Map 中只有 一个 条目具有一个键,一个键在任何 Map 实现中仅出现 一次。
-
我知道,我假设 HashMap 的键是 Map.Entry 的实例。如果您想计算跨多个 Map 的每个键值条目的总数,就会发生这种情况。
-
我看不出这将如何影响这种情况,除非您想将所有地图中的所有 Map.Entry 放在一个地图中以对它们进行计数,但这显然是错误的。
-
虽然
Map.Entry的hashCode定义(以及由此而来的hashCode的Map定义)确实存在很大问题,但很难找到@ 987654337@ 或Map.Entry可用作另一个Map的键(至少在结构良好的代码中)。