is there a bidirectional hashmap for kotlin?
If not - what is the best way to express this in kotlin?
Including guava to get the BiMap from there feels like shooting with a very big gun on a very little target - no solution that I can imagine currently feels right - the best thing I have in mind is to write a custom class for it
I need a simple BiMap implementation too so decided to create a little library called bimap.
The implementation of BiMap is quite straightforward but it contains a tricky part, which is a set of entries, keys and values. I'll try to explain some details of the implementation but you can find the full implementation on GitHub.
First, we need to define interfaces for an immutable and a mutable BiMaps.
interface BiMap<K : Any, V : Any> : Map<K, V> {
override val values: Set<V>
val inverse: BiMap<V, K>
}
interface MutableBiMap<K : Any, V : Any> : BiMap<K, V>, MutableMap<K, V> {
override val values: MutableSet<V>
override val inverse: MutableBiMap<V, K>
fun forcePut(key: K, value: V): V?
}
Please, notice that BiMap.values returns a Set instead of a Collection. Also BiMap.put(K, V) throws an exception when the BiMap already contains a given value. If you want to replace pairs (K1, V1) and (K2, V2) with (K1, V2) you need to call forcePut(K, V). And finally you may get an inverse BiMap to access its keys by values.
The BiMap is implemented using two regular maps:
val direct: MutableMap<K, V>
val reverse: MutableMap<V, K>
The inverse BiMap can be created by just swapping the direct and the reverse maps. My implementation provides an invariant bimap.inverse.inverse === bimap but that's not necessary.
As mentioned earlier the forcePut(K, V) method can replace pairs (K1, V1) and (K2, V2) with (K1, V2). First it checks what the current value for K1 is and removes it from the reverse map. Then it finds a key for value V2 and removes it from the direct map. And then the method inserts the given pair to both maps. Here's how it looks in code.
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
}
Implementations of Map and MutableMap methods are quite simple so I will not provide details for them here. They just perform an operation on both maps.
The most complicated part is entries, keys and values. In my implementation I create a Set that delegates all method invocations to direct.entries and handle modification of entries. Every modification happens in a try/catch block so that the BiMap remains in consistent state when an exception is thrown. Moreover, iterators and mutable entries are wrapped in similar classes. Unfortunately, it makes iteration over entries much less efficient because an additional MutableMap.MutableEntry wrapper is created on every iteration step.
If speed is not a priority ( O(n) complexity ) you can create an extension function: map.getKey(value)
/**
* Returns the first key corresponding to the given [value], or `null`
* if such a value is not present in the map.
*/
fun <K, V> Map<K, V>.getKey(value: V) =
entries.firstOrNull { it.value == value }?.key
FWIW, you can get the inverse of the map in Kotlin using an extension function:
fun <K, V> Map<K, V>.inverseMap() = map { Pair(it.value, it.key) }.toMap()
The map operator can be used to iterate over the List of key-value pairs in the Map, then convert back to a map using .toMap().
Well, you are right - as it stated in a similar question for Java "Bi-directional Map in Java?", Kotlin does not have BiMap out of the box.
The workarounds include using Guava and creating a custom class using two usual maps:
class BiMap<K, V>() {
private keyValues = mutableMapOf<K, V>()
private valueKeys = mutableMapOf<V, K>()
operator fun get(key: K) = ...
operator fun get(value: V) = ...
...
}
This solution should not be slower or take more memory than a more sophisticated one. Although I am not sure what happens when K is the same as V.
The cleanest solution to to use Guava and create an extension function that turns a Map into a BiMap. This follows the semantics of Kotlin's other Map conversions as well. Although Guava might have a bit of overhead, you gain the flexibility to add more extension functions wrappers in the future. You can always remove Guava in the future and replace the extension function with another implementation.
First declare your extension function.
fun <K, V> Map<K, V>.toBiMap() = HashBiMap.create(this)
Then use it like this:
mutableMapOf("foo" to "bar", "me" to "you").toBiMap()
Related
Hello I am trying to add a custom iterator for example to a Pair class from kotlin package to be able to use instance of that class in a for loop
Let's assume this is what I want to be able to do:
val pair: Pair<Int, String> = Pair(1, "sample data")
for (element in pair) {
println(element)
}
I know that there are plenty other ways to print elements from a pair but I specifically want to be able to use pair in a for loop and I need to add iterator() object with next() and hasNext() methods implementation to Pair class
You can do this by providing the iterator() operator for your object, either as a member function or extension function. Example using an extension function:
fun main() {
val pair: Pair<Int, String> = Pair(1, "sample data")
for (element in pair) {
println(element)
}
}
operator fun <T> Pair<T, T>.iterator(): Iterator<T> = listOf(first, second).iterator()
However, you need to be aware that this way you partially lose strongly typing. element can only be a common supertype of all elements, in most cases simply Any?.
You can read more about this in the official documentation: https://kotlinlang.org/docs/control-flow.html#for-loops
val m = mapOf<String, Int>()
m.contains("Foo")
m.containsKey("Bar")
In Kotlin, there are two methods for Map to check whether the map has specified key: contains and containsKey
I know that key in m is the idiomatic way to check key existence, but I wonder why they have two methods doing same function. Do they have any differences between them? Or are they just some sort of legacy code for compatibility?
They are equivalent. This is the contains method implementation:
#kotlin.internal.InlineOnly
public inline operator fun <#kotlin.internal.OnlyInputTypes K, V> Map<out K, V>.contains(key: K): Boolean = containsKey(key)
According docs:
This method (contains) allows to use the x in map syntax for checking whether
an object is contained in the map.
There is no difference between these methods in Map
contains is just generic function, used in different collections with different behaviour (Example: contains object in Collection, but key in Map)
containsKey and containsValue are Maps specific functions
But contains in Map is just a wrapper for containsKey source code:
public inline operator fun <#kotlin.internal.OnlyInputTypes K, V> Map<out K, V>.contains(key: K): Boolean = containsKey(key)
I'm confused about what is going on with the following code. task.yield is a hashmap from a to b, and store.put is a suspend function which takes an a and a b. The first way of iterating thru the map works with no issue, as does the second. The third way, which feels to me like the most natural way to do the iteration and was what I wrote initially, causes kotlin to complain that suspend functions can be called only within a coroutine body.
I'm guessing this has to do with how forEaching on a map works (as opposed to a list maybe?) but I don't really understand what the issue is.
launch{
// Kotlin is perfectly happy with this
for(elt in task.yield.keys){
store.put(elt,task.yield[elt]!!)
}
// and this
task.yield.keys.forEach {
store.put(it,task.yield[it]!!)
}
// This makes kotlin sad. I'm not sure why
task.yield.forEach { t, u ->
store.put(t, u)
}
}
Edit: I've just noticed that the list forEach is an inline function while the map one I'm trying to use isn't. I'm guessing this is the issue.
Indeed, the overload of Map#forEach that accepts a (K, V) -> Unit (a BiConsumer<? super K, ? super V>) is not part of the Kotlin standard libraries but rather of the JDK itself (Map#forEach). This explains why anything executing within this block is not inlined and therefore not part of the enclosing "suspending context".
There is a very similar function that Kotlin provides that you can make use of:
inline fun <K, V> Map<out K, V>.forEach(action: (Entry<K, V>) -> Unit)
Performs the given action on each entry.
kotlin-stdlib / kotlin.collections / forEach
This accepts an Entry<K, V>, so you can simply destructure it within the lambda:
task.yield.forEach { (t, u) /* <-- this */ ->
store.put(t, u)
}
As http://kotlinlang.org/docs/reference/multi-declarations.html#multi-declarations says:
The component1() and component2() functions are another example of the principle of conventions widely used in Kotlin (see operators like + and *, for-loops etc.). Anything can be on the right-hand side of a destructuring declaration, as long as the required number of component functions can be called on it. And, of course, there can be component3() and component4() and so on.
For example,
class Pair<K, V>(val first: K, val second: V) {
operator fun component1(): K {
return first
}
operator fun component2(): V {
return second
}
}
But I think it's very boring to manually input these methods. So is there any way to generate these methods in IDEA.
You can let IDEA help you. Take the following example, a simple class without componentX functions:
class Ex(val a: Int, val b: Int)
And then a destructuring of it, which does not compile:
val (a,b) = Ex(1,2)
IDEA will give you some hints on how to fix the problem if you use the shortcut "Show intention action":
Try to use data classes.
In such case, you class should looks like:
data class Pair<K, V>(val first: K, val second: V)
https://kotlinlang.org/docs/reference/data-classes.html
Quick Kotlin best practices question, as I couldn't really work out the best way to do this from the documentation.
Assume I have the following nested map (typing specified explicitly for the purpose of this question):
val userWidgetCount: Map<String, Map<String, Int>> = mapOf(
"rikbrown" to mapOf(
"widgetTypeA" to 1,
"widgetTypeB" to 2))
Can the following mode be any more succinct?
fun getUserWidgetCount(username: String, widgetType: String): Int {
return userWidgetCount[username]?.get(widgetType)?:0
}
In other words, I want to return the user widget count iff the user is known and they have an entry for that widget type, otherwise zero. In particular I saw I can use [] syntax to access the map initially, but I couldn't see a way to do this at the second level after using ?..
I would use an extension operator method for that.
// Option 1
operator fun <K, V> Map<K, V>?.get(key: K) = this?.get(key)
// Option 2
operator fun <K, K2, V> Map<K, Map<K2, V>>.get(key1: K, key2: K2): V? = get(key1)?.get(key2)
Option 1:
Define an extension that provides get operator for nullable map. In Kotlin's stdlib such approach appears with Any?.toString() extension method.
fun getUserWidgetCount(username: String, widgetType: String): Int {
return userWidgetCount[username][widgetType] ?: 0
}
Option 2:
Create a special extension for map of maps. In my opinion, it is better because it shows the contract of the map of maps better than two gets in a row.
fun getUserWidgetCount(username: String, widgetType: String): Int {
return userWidgetCount[username, widgetType] ?: 0
}