在某些情况下,map中使用的键对象不会覆盖Object中的hashCode()和equals(),例如,使用套接字连接或java.lang.Class作为键。
答案 0 :(得分:5)
如果未在关键对象上覆盖equals()
和hashCode()
,则HashMap和IdentityHashMap应具有相同的语义。默认的equals()实现使用引用语义,默认的hashCode()
是对象的系统标识哈希码。
这仅在对象的不同实例被视为逻辑上相等的情况下才有害。例如,如果您的密钥是:
,则不希望使用IdentityHashMapnew 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
密钥类的恶意实现可以覆盖hashCode
和equals
是恶意的。例如,他们可以声称对不同实例的平等,获取对与其进行比较的其他实例的引用,等等。我建议通过保持安全并使用IdentityHashMap
所需的身份语义来打破标准实践。很少有理由在已经比较超类的子类中改变相等的含义。我想最可能的情况是破坏的非对称代理。
IdentityHashMap
的实施与HashMap
完全不同。它使用线性探测而不是Entry
个对象作为链中的链接。这会导致对象数量略有减少,尽管总内存使用量差异很小。我没有任何可以引用的良好性能统计数据。使用(非重写)Object.hashCode
和System.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被认为是相等的。)
总之,我的答案是:
答案 3 :(得分:1)
虽然没有理论上的问题,但除非您有明确的理由使用它,否则应避免使用IdentityHashMap
。它在一般情况下没有提供明显的性能或其他好处,当你不可避免地开始在地图中引入做覆盖equals()
和hashCode()
的对象时,你最终会有微妙的,难以诊断的错误。
如果您认为出于性能原因需要IdentityHashMap
,请在进行切换之前使用分析器确认怀疑。我的猜测是你会发现很多其他更优化的机会,既更安全又有更大的不同。
答案 4 :(得分:0)
据我所知,带有坏键的hashmap的唯一问题是使用非常大的哈希映射 - 你的密钥可能非常糟糕,你得到o(n)检索时间,而不是o(1)。如果它确实破坏了其他任何东西,我会有兴趣听到它:)