Operations on Map fails when using ByteArray as key


#1

I was using ByteArray as key for my Map in the project I am working on and noticed that it always returns false when I check containsKey on the map with the exact same key. Then I tried to make it into a minimal working example like the following:

val key1 = byteArrayOf(49, 53, 48, 56, 55, 52, 48, 58, 100, 48, 56, 50, 52, 57, 97, 48, 52, 49, 50, 48, 100, 97, 101, 50, 96, 57, 56, 100)
val key2 = byteArrayOf(49, 53, 48, 56, 55, 52, 48, 58, 100, 48, 56, 50, 52, 57, 97, 48, 52, 49, 50, 48, 100, 97, 101, 50, 96, 57, 56, 100)
val myMap = mutableMapOf<ByteArray, Int>(key1 to 1, key2 to 2)
myMap.containsKey(key1) 

The same implementation in my project returns false, but it returns true in this MWE. I noticed a similar behavior when I do myMap.putIfAbsent(key, defaultValue). In my project, it adds another entry to the Map with exactly the same key but when I construct a MWE, it does not.

Here’s a screen shot of the map with 2 identical keys:

I know from this post that you cannot use plain == to compare 2 byte arrays. However, the same behavior in Map makes me have to come up with ugly work-arounds.

And I am baffled by the inconsistency in behavior between in my project and in the MWE.


#2

The reason is that arrays do not provide structural equality operation in JVM. So even if two arrays have the same contents they won’t be considered equal.

The keys in your screenshot may have the same contents but are not identical, try to compare them as key1.equals(key2) as Map does when it checks whether it already has a key.

The workaround is to introduce a wrapper class around ByteArray and implement its equals using the structural equality operation:

class ByteArrayKey(private val bytes: ByteArray) {
    override fun equals(other: Any?): Boolean =
            this === other || other is ByteArrayKey && this.bytes contentEquals other.bytes
    override fun hashCode(): Int = bytes.contentHashCode()
    override fun toString(): String = bytes.contentToString()
}