Let’s review simple example:
val map = mapOf<String, Int>("test1" to 1, "test2" to 2)
val sorted = map.toSortedMap { a, b ->
map[b]?.compareTo(map[a] ?: 0) ?: 0
}
println(sorted)
The output will be {test2=2, test1=1}
Then if we change to
val map = mapOf<String, Int>("test1" to 1, "test2" to 1)
val sorted = map.toSortedMap { a, b ->
map[b]?.compareTo(map[a] ?: 0) ?: 0
}
println(sorted)
Then the size of sorted map suddenly shrink to 1 instead of 2 and show output:
{test1=1}
in an other hand if I sort map like this:
val sorted = map.toSortedMap()
it will keep sorted map size to original 2 elements with output:
{test1=1, test2=1}
Can anyone explain why?
Take a closer look into the TreeMap
implementation. This map doesn’t use hashCode()
& equals()
to verify equality of the elements, but the result of comparison itself. The comparison value of 0
means that both elements are equal in this situation, therefore ‘duplicates’ are simply cut off.
As for more practical advice how to fix it, it will depend on what you what to achieve from API perspective.
1 Like
My guess is that you wanted a map-like structure where you access elements via String
keys with constant time, but when iterating you want an order going by values? You can for example implement map like this:
class MapSortedByValues<K, V : Comparable<V>>(sourceMap: Map<K, V>) : Map<K, V> {
private val orderedEntries = sourceMap.entries.sortedBy(Map.Entry<K, V>::value)
private val indexMap = orderedEntries.withIndex().associate { (index, entry) -> entry.key to index }
private val orderedValues = orderedEntries.map(Map.Entry<K, V>::value)
override val entries get() = object : Set<Map.Entry<K, V>> {
override val size get() = orderedEntries.size
override fun contains(element: Map.Entry<K, V>) = element in orderedEntries
override fun containsAll(elements: Collection<Map.Entry<K, V>>) = orderedEntries.containsAll(elements)
override fun isEmpty() = orderedEntries.isEmpty()
override fun iterator() = orderedEntries.iterator()
}
override val keys get() = indexMap.keys
override val size get() = indexMap.size
override val values get() = orderedValues
override fun containsKey(key: K) = key in indexMap
override fun containsValue(value: V) = value in orderedValues
override fun get(key: K) = indexMap[key]?.let(orderedValues::get)
override fun isEmpty() = indexMap.isEmpty()
override fun hashCode() = orderedEntries.hashCode()
override fun equals(other: Any?) = this === other || other is MapSortedByValues<*, *> && orderedEntries == other.orderedEntries
override fun toString() = orderedEntries.joinToString(separator = ", ", prefix = "{", postfix = "}") { "${it.key}=${it.value}" }
}
fun main() {
val map = mapOf("test1" to 2, "test2" to 1)
println(map)
val sorted = MapSortedByValues(map)
println(sorted)
}