BiMap / hashmap bidireccional en Kotlin

¿hay un hashmap bidireccional para kotlin? Si no, ¿cuál es la mejor manera de express esto en kotlin? Incluir guayaba para sacar el BiMap de allí se siente como disparar con un arma muy grande contra un objective muy pequeño -ninguna solución que puedo imaginar que actualmente se sienta bien-, lo mejor que tengo en mente es escribir una class personalizada para ello.

También necesito una implementación simple de BiMap , así que decidí crear una pequeña biblioteca llamada bimap .

La implementación de BiMap es bastante sencilla, pero contiene una parte difícil, que es un set de inputs, keys y valores. Trataré de explicar algunos detalles de la implementación, pero puede encontrar la implementación completa en GitHub .

Primero, necesitamos definir interfaces para BiMap s inmutables y mutables.

 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? } 

Tenga en count que BiMap.values devuelve un Set lugar de una Collection . También BiMap.put(K, V) arroja una exception cuando el BiMap ya contiene un valor dado. Si quiere replace pares (K1, V1) y (K2, V2) con (K1, V2) , necesita llamar a forcePut(K, V) . Y finalmente puede get un BiMap inverso para acceder a sus keys por valores.

BiMap se implementa utilizando dos maps regulares:

 val direct: MutableMap<K, V> val reverse: MutableMap<V, K> 

El BiMap inverso se puede crear simplemente intercambiando los maps direct e reverse . Mi implementación proporciona un bimap.inverse.inverse === bimap invariante, pero eso no es necesario.

Como se mencionó anteriormente, el forcePut(K, V) puede replace pares (K1, V1) y (K2, V2) con (K1, V2) . Primero comtesting cuál es el valor actual para K1 y lo elimina del map reverse . Luego encuentra una key para el valor V2 y la elimina del map direct . Y luego el método inserta el par dado en ambos maps. Así es como se ve en el código.

 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 } 

Las implementaciones de los methods Map y MutableMap son bastante simples, así que no les proporcionaré detalles aquí. Simplemente realizan una operación en ambos maps.

La parte más complicada son las entries , las keys y los values . En mi implementación, creo un Set que delega todas las invocaciones de methods en direct.entries y maneja la modificación de las inputs. Cada modificación ocurre en un bloque try / catch para que BiMap permanezca en estado consistente cuando se lanza una exception. Además, los iteradores y las inputs mutables están envueltos en classs similares. Desafortunadamente, hace que la iteración sobre las inputs sea mucho less eficiente porque se crea un contenedor MutableMap.MutableEntry adicional en cada paso de iteración.

Bueno, tienes razón, como decía en una pregunta similar para Java "¿ Mapa bidireccional en Java? ", Kotlin no tiene BiMap fuera de la caja.

Las soluciones incluyen usar Guava y crear una class personalizada usando dos maps habituales:

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

Esta solución no debería ser más lenta o tomar más memory que una más sofisticada. Aunque no estoy seguro de qué sucede cuando K es igual a V

Si la velocidad no es una prioridad, puede usar la function de extensión: map. findKeyByValue (valor)

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

La solución más limpia para utilizar Guava y crear una function de extensión que convierte un map en un BiMap. Esto sigue la semántica de las otras conversiones de maps de Kotlin también. Aunque Guava puede tener un poco de sobrecarga, usted gana la flexibilidad de agregar más envoltorios de funciones de extensión en el futuro. Siempre puede eliminar Guava en el futuro y replace la function de extensión con otra implementación.

Primero declara tu function de extensión.

fun <K, V> Map<K, V>.toBiMap() = HashBiMap.create(this)

Entonces úsalo así:

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

  • Herencias internas Herencia en Kotlin
  • ¿Hay alguna forma de implementar la aplicación del server back-end (Kotlin) en Firebase?
  • Genéricos de Kotlin
  • UNRESOLVED_REFERENCE en widget (TextView) resuelto al elemento error en android-kotlin
  • ¿Cómo puedo agregar TeaVM a mi proyecto libGDX existente?
  • Kotlin Constructor Crash
  • Tipo no coincidente: ¿tipo inferido es Cadena? pero se esperaba String en kotlin
  • Pausa / Reanudar un timer / retraso en RX
  • ¿Cómo hacer trabajo @Autowinetworking en una class regular?
  • Kotlin apply () post de pelusa de extensión en Android Studio 3.0-alpha8
  • Kotlin anula la diversión con subtipo