hashmap kotlin 的不区分大小写的键?
Case insensitive key for hashmap kotlin?
我有一个以字符串为键、以整数为值的哈希图。我希望按键不区分大小写。
val items = HashMap<String, Int>()
items["key1"] = 90
items["Key1"] = 80
items["C"] = 70
for ((k, v) in items) {
println("$k = $v")
}
这将 key1 和 Key1 作为单独的条目
为此,您需要提供一些扩展功能,以某种定义的方式放置和获取条目(例如,每次使用 lowercase()
字符串的方法)保持键不区分大小写
fun HashMap<String, Int>.putInsensitive(k: String, v: Int) {
this[k.lowercase()] = v
}
fun HashMap<String, Int>.getInsensitive(k: String, v: Int): Int? = this[k.lowercase()]
或者提供自己的Map
接口实现(甚至可以继承自HashMap)
class InsensitiveHashMap<V> : HashMap<String, V>() {
override fun put(key: String, value: V): V? = super.put(key.lowercase(), value)
override fun get(key: String): V? = return super.get(key.lowercase())
}
尽管您可以使用 HashMap 的扩展来伪造它(如另一个答案中所述),但我认为这不是一个好的解决方案:为了使地图的行为完全一致,您可能需要重写很多方法。 (例如,如果您通过其 keySet 更新地图,则很难确保它保持不区分大小写。 在此过程中可能还有更多问题。)
在大多数情况下,您实际上并不需要特定的 HashMap
— 您只需要 Map
implementation. And so an alternative is to use another type of map that lets you provide your own Comparator
,例如:
val items = TreeMap<String, Int>{ a, b ->
a.toLowerCase().compareTo(b.toLowerCase())
}
TreeMap
is an implementation of SortedMap
— 一种特殊类型的 Map
,它使键保持有序:它们的自然顺序或您提供的顺序。在这种情况下,我给出了一个简单的 Comparator
实现,它比较两个字符串的小写版本。
根据这个定义,问题中的其余代码运行良好,并打印出:
C = 70
key1 = 80
……即它已经认识到“Key1”应该被视为与“key1”相同,并更新了那个值而不是添加一个新值。我认为这就是您想要的行为。
您不需要考虑地图已排序的事实;那只是一个额外的好处。您仍然可以像对待任何其他 Map
实现一样对待它,一切都应该有效。
(这是另一个例子,说明为什么最好针对接口而不是实现进行编程。‹ 如果您编写的代码可以使用任何 Map
实现,则可以给它一个 HashMap
或 TreeMap
或任何其他类型的地图,没有任何更改。)
@m.antkowicz 的答案变体,您实际上可以“覆盖”HashMap
本身的 get / put 运算符(并使其成为一个很棒的 footgun):
operator fun HashMap<String, Int>.get(k: String): Int? = this[k.toLowerCase()]
operator fun HashMap<String, Int>.set(k: String, v: Int): Int? {
val originalK = this[k.toLowerCase()]
this.put(k.toLowerCase(), v )
return originalK
}
现在您可以:
fun main() {
val items = HashMap<String, Int>()
items["key1"] = 90
items["Key1"] = 80
items["C"] = 70
for ((k, v) in items) {
println("$k = $v")
}
// Prints:
// key1 = 80
// c = 70
}
我有一个以字符串为键、以整数为值的哈希图。我希望按键不区分大小写。
val items = HashMap<String, Int>()
items["key1"] = 90
items["Key1"] = 80
items["C"] = 70
for ((k, v) in items) {
println("$k = $v")
}
这将 key1 和 Key1 作为单独的条目
为此,您需要提供一些扩展功能,以某种定义的方式放置和获取条目(例如,每次使用 lowercase()
字符串的方法)保持键不区分大小写
fun HashMap<String, Int>.putInsensitive(k: String, v: Int) {
this[k.lowercase()] = v
}
fun HashMap<String, Int>.getInsensitive(k: String, v: Int): Int? = this[k.lowercase()]
或者提供自己的Map
接口实现(甚至可以继承自HashMap)
class InsensitiveHashMap<V> : HashMap<String, V>() {
override fun put(key: String, value: V): V? = super.put(key.lowercase(), value)
override fun get(key: String): V? = return super.get(key.lowercase())
}
尽管您可以使用 HashMap 的扩展来伪造它(如另一个答案中所述),但我认为这不是一个好的解决方案:为了使地图的行为完全一致,您可能需要重写很多方法。 (例如,如果您通过其 keySet 更新地图,则很难确保它保持不区分大小写。 在此过程中可能还有更多问题。)
在大多数情况下,您实际上并不需要特定的 HashMap
— 您只需要 Map
implementation. And so an alternative is to use another type of map that lets you provide your own Comparator
,例如:
val items = TreeMap<String, Int>{ a, b ->
a.toLowerCase().compareTo(b.toLowerCase())
}
TreeMap
is an implementation of SortedMap
— 一种特殊类型的 Map
,它使键保持有序:它们的自然顺序或您提供的顺序。在这种情况下,我给出了一个简单的 Comparator
实现,它比较两个字符串的小写版本。
根据这个定义,问题中的其余代码运行良好,并打印出:
C = 70
key1 = 80
……即它已经认识到“Key1”应该被视为与“key1”相同,并更新了那个值而不是添加一个新值。我认为这就是您想要的行为。
您不需要考虑地图已排序的事实;那只是一个额外的好处。您仍然可以像对待任何其他 Map
实现一样对待它,一切都应该有效。
(这是另一个例子,说明为什么最好针对接口而不是实现进行编程。‹ 如果您编写的代码可以使用任何 Map
实现,则可以给它一个 HashMap
或 TreeMap
或任何其他类型的地图,没有任何更改。)
@m.antkowicz 的答案变体,您实际上可以“覆盖”HashMap
本身的 get / put 运算符(并使其成为一个很棒的 footgun):
operator fun HashMap<String, Int>.get(k: String): Int? = this[k.toLowerCase()]
operator fun HashMap<String, Int>.set(k: String, v: Int): Int? {
val originalK = this[k.toLowerCase()]
this.put(k.toLowerCase(), v )
return originalK
}
现在您可以:
fun main() {
val items = HashMap<String, Int>()
items["key1"] = 90
items["Key1"] = 80
items["C"] = 70
for ((k, v) in items) {
println("$k = $v")
}
// Prints:
// key1 = 80
// c = 70
}