BiMap /双向HashMap在Kotlin

有没有kotlin的双向哈希映射? 如果不是 – 在kotlin中expression这个最好的方法是什么? 包括番石榴从那里得到BiMap感觉就像在一个很小的目标上用一个很大的枪射击 – 没有我能想象的解决方案现在感觉是正确的 – 我想到的最好的事情是写一个自定义的类

我也需要一个简单的BiMap实现,所以决定创建一个名为bimap的小库。

BiMap的实现非常简单,但它包含一个棘手的部分,它是一组条目,键和值。 我将尝试解释一些实现的细节,但是你可以在GitHub上find完整的实现。

首先,我们需要为一个不可变和可变的BiMap定义接口。

 interface BiMap : Map { override val values: Set val inverse: BiMap } interface MutableBiMap : BiMap, MutableMap { override val values: MutableSet override val inverse: MutableBiMap fun forcePut(key: K, value: V): V? } 

请注意, BiMap.values返回一个Set而不是一个Collection 。 当BiMap已经包含一个给定的值时BiMap.put(K, V)抛出一个exception。 如果你想用(K1, V1)替代对(K1, V1)(K2, V2) ,你需要调用forcePut(K, V) 。 最后,你可能会得到一个逆BiMap来通过值访问它的键。

BiMap使用两个常规映射来实现:

 val direct: MutableMap val reverse: MutableMap 

可以通过交换direct映射和reverse映射来创建逆BiMap 。 我的实现提供了一个不变的bimap.inverse.inverse === bimap但这不是必需的。

如前所述, forcePut(K, V)方法可以用(K1, V1)代替对(K1, V1)(K2, V2) (K1, V2) 。 首先检查K1的当前值是什么,并将其从reverse映射中移除。 然后find值为V2的键并将其从direct映射中移除。 然后该方法将给定的一对插入到两个地图中。 这是代码中的外观。

 override fun forcePut(key: K, value: V): V? { val oldValue = direct.put(key, value) oldValue?.let { reverse.remove(it) } val oldKey = reverse.put(value, key) oldKey?.let { direct.remove(it) } return oldValue } 

MapMutableMap方法的实现非常简单,所以我不在这里提供细节。 他们只是在两个地图上执行操作。

最复杂的部分是entrieskeysvalues 。 在我的实现中,我创建了一个Set所有方法调用委托给direct.entries并处理条目修改的Set 。 每个修改都发生在try / catch块中,以便在抛出exception时BiMap保持一致状态。 而且,迭代器和可变条目被包装在类似的类中。 不幸的是,它使迭代条目效率低得多,因为在每个迭代步骤中都会创建一个额外的MutableMap.MutableEntry包装器。

那么你是对的 – 正如它在类似的问题中提到的Java“ 双向映射Java ”一样,Kotlin没有开箱即用的BiMap。

解决方法包括使用Guava和使用两个通常的地图创建自定义类:

 class BiMap() { private keyValues = mutableMapOf() private valueKeys = mutableMapOf() operator fun get(key: K) = ... operator fun get(value: V) = ... ... } 

这个解决方案不应该比更复杂的解决方案更慢或者占用更多的内存。 虽然我不确定当KV相同时会发生什么。

如果速度不是优先级,您可以使用扩展function:地图。 findKeyByValue (value)

 fun  Map.findKeyByValue(searchValue: Value): Key? { for ((key, value) in this) { if (value == searchValue) return key } return null } 

使用番石榴最简洁的解决方案, 创建一个扩展function,将地图变成一个BiMap。 这也遵循Kotlin其他地图转换的语义。 尽管Guava可能会有一些开销,但您将来可以灵活地添加更多的扩展函数包装器。 您可以随时删除番石榴,并将扩展function替换为其他实施。

首先声明你的扩展function。

fun Map.toBiMap() = HashBiMap.create(this)

然后像这样使用它:

mutableMapOf("foo" to "bar", "me" to "you").toBiMap()