Home > Back-end >  How to force object equivalence for keys in a Kotlin hashmap?
How to force object equivalence for keys in a Kotlin hashmap?

Time:11-22

I have a class 'Foo' (not under my control) which I wish to use as a key in a kotlin (java) hashmap. The problem is that the 'equals' method for 'Foo' does value equivalence. For my situation value equivalence is too loose. I need object equivalence.

What are the ways to force force the use of object equivalence on the keys?

I am thinking something like...

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}")
}

This results in a fooMap with only one entry, when I expect two.

[Foo(prop=common value)] [Bar(prop=different B)]

CodePudding user response:

Consider using IdentityHashMap - it is the same map, but which compares only references of the keys.

Also note, to effectively use regular HashMap, the key class must respect not only equals, but also hashCode.

CodePudding user response:

I will be using @AterLux answer but I am posting what I had been using for comments.

data class Identity<T>(private val delegate: T) {
    override fun equals(other: Any?): Boolean {
        return delegate === other
    }
}
fun <K,V> mutableIdentityMapOf(): MutableMap<Identity<K>,V> {
    return mutableMapOf()
}


data class Foo(val prop: String)
data class Bar(val prop: String)

fun main() {
    val fooMap = mutableIdentityMapOf<Foo, Bar>()

    val fooA = Foo("common value")
    val fooB = Foo("common value")

    fooMap[Identity(fooA)] = Bar("different A")
    fooMap[Identity(fooB)] = Bar("different B")
    println("${fooMap.keys} ${fooMap.values}")
}

CodePudding user response:

For your specific case and when targeting JVM, IdentityHashMap is probably the best choice, as suggested by @AterLux .

If we need multiplatform solution or we have a more generic case of changing the equals() logic of a third party class, we can simply use our own wrapper as a key:

data class FooKey(val foo: Foo) {
    override fun equals(other: Any?) = foo === (other as? FooKey)?.foo
}


fun main() {
    val fooMap = mutableMapOf<FooKey, Bar>()

    val fooA = Foo("common value")
    val fooB = Foo("common value")

    fooMap[FooKey(fooA)] = Bar("different A")
    fooMap[FooKey(fooB)] = Bar("different B")
    println("${fooMap.keys} ${fooMap.values}")
}
  • Related