如何强制Kotlin HashMap的键使用对象等价性?
问题
我有一个不受控制的类Foo,希望将其用作Kotlin(Java)HashMap的键。问题在于Foo的equals方法实现的是值等价性,这对我的场景来说过于宽松,我需要的是对象等价性。我尝试了如下代码:
data class Foo(val prop: String) data class Bar(val prop: String) fun main() { val fooMap = mutableMapOf<Any, Bar>() val fooA = Foo("common value") val fooB = Foo("common value") fooMap[fooA] = Bar("different A") fooMap[fooB] = Bar("different B") println("${fooMap.keys} ${fooMap.values}") }
结果fooMap中只有一个条目,但我期望有两个:
[Foo(prop=common value)] [Bar(prop=different B)]
解决方案
1. 使用Java的IdentityHashMap
IdentityHashMap完全基于对象的引用相等性判断键是否相同,直接忽略类自身实现的equals和hashCode方法,在Kotlin中可直接使用:
data class Foo(val prop: String) data class Bar(val prop: String) fun main() { val fooMap = java.util.IdentityHashMap<Foo, Bar>() val fooA = Foo("common value") val fooB = Foo("common value") fooMap[fooA] = Bar("different A") fooMap[fooB] = Bar("different B") println("${fooMap.keys} ${fooMap.values}") }
运行后会输出两个独立条目,符合预期。
2. 自定义引用相等的包装类
创建不重写equals和hashCode的包装类(默认实现基于引用相等),将Foo实例作为包装类属性,用包装类作为Map的键:
data class Foo(val prop: String) data class Bar(val prop: String) class IdentityWrapper<T>(val value: T) fun main() { val fooMap = mutableMapOf<IdentityWrapper<Foo>, Bar>() val fooA = Foo("common value") val fooB = Foo("common value") fooMap[IdentityWrapper(fooA)] = Bar("different A") fooMap[IdentityWrapper(fooB)] = Bar("different B") println("${fooMap.keys.map { it.value }} ${fooMap.values}") }
这种方法可继续使用Kotlin标准库的MutableMap实现,通过包装类规避原类的equals逻辑。
3. 使用Kotlin的引用相等策略(1.4+)
从Kotlin 1.4开始,mutableMapOf支持传入EqualityPolicy.reference,直接指定Map使用引用相等判断键:
data class Foo(val prop: String) data class Bar(val prop: String) fun main() { val fooMap = mutableMapOf<Foo, Bar>(equalityPolicy = EqualityPolicy.reference) val fooA = Foo("common value") val fooB = Foo("common value") fooMap[fooA] = Bar("different A") fooMap[fooB] = Bar("different B") println("${fooMap.keys} ${fooMap.values}") }
该方法语法简洁,无需依赖Java集合或自定义包装类,是Kotlin环境下的首选方案之一。
内容的提问来源于stack exchange,提问作者phreed
相关产品推荐
相关产品推荐

