Java HashMap或IdentityHashMap

时间:2010-09-16 02:16:24

标签: java

在某些情况下,map中使用的键对象不会覆盖Object中的hashCode()和equals(),例如,使用套接字连接或java.lang.Class作为键。

  1. 将这些对象用作HashMap中的键是否存在任何潜在缺陷?
  2. 在这些情况下我应该使用IdentityHashMap吗?

5 个答案:

答案 0 :(得分:5)

如果未在关键对象上覆盖equals()hashCode(),则HashMap和IdentityHashMap应具有相同的语义。默认的equals()实现使用引用语义,默认的hashCode()是对象的系统标识哈希码。

这仅在对象的不同实例被视为逻辑上相等的情况下才有害。例如,如果您的密钥是:

,则不希望使用IdentityHashMap
new Integer(1)

new Integer(1)

因为这些是技术上不同的Integer类实例。 (你应该真的使用Integer.valueOf(1),但那是偏离主题的。)

Class因为键应该没问题,除非在非常特殊的情况下(例如,hibernate ORM库在运行时生成类的子类以实现代理。)作为开发人员,我会怀疑将Map中的Connection个对象存储为密钥的代码(如果您正在管理数据库连接,可能应该使用连接池?)。它们是否可以工作取决于实现(因为Connection只是一个接口)。

此外,重要的是要注意HashMap期望equals()hashCode()确定保持不变。特别是,如果您实现了一些在密钥对象上使用可变字段的自定义hashCode(),则更改密钥字段可能会使密钥在HashMap的错误哈希表桶中“丢失”。在这些情况下,您可以使用IdentityHashMap(取决于对象和您的特定用例),或者您可能只需要一个不同的equals()/hashCode()实现。

答案 1 :(得分:3)

从移动代码安全的角度来看,有些情况下需要使用IdentityHashMap或类似内容。非final密钥类的恶意实现可以覆盖hashCodeequals是恶意的。例如,他们可以声称对不同实例的平等,获取对与其进行比较的其他实例的引用,等等。我建议通过保持安全并使用IdentityHashMap所需的身份语义来打破标准实践。很少有理由在已经比较超类的子类中改变相等的含义。我想最可能的情况是破坏的非对称代理。

IdentityHashMap的实施与HashMap完全不同。它使用线性探测而不是Entry个对象作为链中的链接。这会导致对象数量略有减少,尽管总内存使用量差异很小。我没有任何可以引用的良好性能统计数据。使用(非重写)Object.hashCodeSystem.identityHashCode之间的性能差异,但几年前就已经解决了。

答案 2 :(得分:1)

在您描述的情况中,HashMap和IdentityHashMap的行为是相同的。

与此形成鲜明对比的是,如果键覆盖equals()和hashCode(),则两个映射的行为会有所不同。

请参阅下面的java.util.IdentityHashMap的javadoc。

  

此类使用哈希表实现Map接口,在比较键(和值)时使用引用相等性代替对象相等性。换句话说,在IdentityHashMap中,当且仅当(k1 == k2)时,两个密钥k1和k2被认为是相等的。 (在正常的Map实现中(如HashMap),当且仅当(k1 == null?k2 == null:k1.equals(k2))时,两个键k1和k2被认为是相等的。)

总之,我的答案是:

  1. 将这些对象用作HashMap中的键是否存在任何潜在的缺陷? - >
  2. 在这些情况下我应该使用IdentityHashMap吗? - >

答案 3 :(得分:1)

虽然没有理论上的问题,但除非您有明确的理由使用它,否则应避免使用IdentityHashMap。它在一般情况下没有提供明显的性能或其他好处,当你不可避免地开始在地图中引入覆盖equals()hashCode()的对象时,你最终会有微妙的,难以诊断的错误。

如果您认为出于性能原因需要IdentityHashMap,请在进行切换之前使用分析器确认怀疑。我的猜测是你会发现很多其他更优化的机会,既更安全又有更大的不同。

答案 4 :(得分:0)

据我所知,带有坏键的hashmap的唯一问题是使用非常大的哈希映射 - 你的密钥可能非常糟糕,你得到o(n)检索时间,而不是o(1)。如果它确实破坏了其他任何东西,我会有兴趣听到它:)