2011-11-09 14 views
19

Estoy buscando una forma de almacenar pares clave-valor. Necesito que la búsqueda sea bidireccional, pero al mismo tiempo necesito almacenar valores múltiples para la misma clave. En otras palabras, algo así como BidiMap, pero para cada clave puede haber múltiples valores. Por ejemplo, debe ser capaz de mantener pares como: "s1" -> 1, "s2" -> 1, "s3" -> 2, y necesito poder asignar el valor asignado a cada tecla, y para cada valor, obtenga todas las claves asociadas con él.Mapa bidireccional de valores múltiples en Java

+3

Habla de la necesidad de tener múltiples valores por clave, pero en su ejemplo no tiene una clave con múltiples valores, sino un valor con dos claves. Probablemente deberías aclarar eso. Si su ejemplo se ajusta a su pregunta, obtendrá mejores respuestas ;-) – pushy

+0

http://www.jguru.com/faq/view.jsp?EID=1317828 aquí puede encontrar cómo crear multimap – maks

+0

@pushy, mismo problema, si invierto el mapa y mantengo los enteros como claves en lugar de como valores, obtengo una asignación de uno a muchos. De todos modos, gracias por la corrección. :) –

Respuesta

19

¿Necesita soporte para relaciones de muchos a muchos? Lo más cercano que puede obtener es Guava 's Multimap como @Mechkov escribió - pero más específicamente Multimap combinación con Multimaps.invertFrom. "BiMultimap" aún no está implementado, pero está an issue solicitando esta función en la biblioteca de Google Guava.

En este punto se tienen pocas opciones:

  1. Si su "BiMultimap" va a constante inmutable - utilizar Multimaps.invertFrom y ImmutableMultimap/ImmutableListMultimap/ImmutableSetMultimap (cada uno de theese tres tiene diferentes valores de la colección de almacenamiento). Algo de código (ejemplo tomado de aplicación que desarrollo, utiliza Enum s) y Sets.immutableEnumSet:

    public class RolesAndServicesMapping { 
        private static final ImmutableMultimap<Service, Authority> SERVICES_TO_ROLES_MAPPING = 
         ImmutableMultimap.<Service, Authority>builder() 
          .put(Service.SFP1, Authority.ROLE_PREMIUM) 
          .put(Service.SFP, Authority.ROLE_PREMIUM) 
          .put(Service.SFE, Authority.ROLE_EXTRA) 
          .put(Service.SF, Authority.ROLE_STANDARD) 
          .put(Service.SK, Authority.ROLE_STANDARD) 
          .put(Service.SFP1, Authority.ROLE_ADMIN) 
          .put(Service.ADMIN, Authority.ROLE_ADMIN) 
          .put(Service.NONE, Authority.ROLE_DENY) 
          .build(); 
    
        // Whole magic is here: 
        private static final ImmutableMultimap<Authority, Service> ROLES_TO_SERVICES_MAPPING = 
          SERVICES_TO_ROLES_MAPPING.inverse(); 
        // before guava-11.0 it was: ImmutableMultimap.copyOf(Multimaps.invertFrom(SERVICES_TO_ROLES_MAPPING, HashMultimap.<Authority, Service>create())); 
    
        public static ImmutableSet<Authority> getRoles(final Service service) { 
         return Sets.immutableEnumSet(SERVICES_TO_ROLES_MAPPING.get(service)); 
        } 
    
        public static ImmutableSet<Service> getServices(final Authority role) { 
         return Sets.immutableEnumSet(ROLES_TO_SERVICES_MAPPING.get(role)); 
        } 
    } 
    
  2. Si realmente quiere que su Multimapa sea modificable, que será difícil de mantener tanto K-> V y V-> Variantes K a menos que solo modifiques kToVMultimap y llames al invertFrom cada vez que quieras tener su copia invertida (y haciendo que esa copia no se pueda modificar para asegurarte de que accidentalmente no modifique vToKMultimap lo que no actualizaría kToVMultimap). Esto no es óptimo, pero debería hacerlo en este caso.

  3. (¿No es su caso, probablemente, como se ha mencionado bono): BiMap interfaz y las clases que implementan tiene .inverse() método que da BiMap<V, K> vista desde BiMap<K, V> y sí después biMap.inverse().inverse(). Si this issue que mencioné antes está hecho, probablemente tendrá algo similar.

  4. (EDIT de octubre de 2016) También puede utilizar new graph API que estará presente en Guava 20:

    En su conjunto, común.gráfico soporta gráficas de las siguientes variedades:

    • gráficos dirigidos
    • grafos no dirigidos
    • nodos y/o bordes con valores asociados (pesos, etiquetas, etc.)
    • gráficos que hacen/no hacer permitir la libre bucles
    • gráficos que dO/no permiten bordes paralelos (gráficos con bordes paralelos a veces se llaman multigrafos)
    • gráficos cuyos nodos/bordes son inserción ordenada, ordenados o no ordenada
-1

Esperanza te tengo

class A { 
    long id; 
    List<B> bs; 
} 

class B { 
    long id; 
    List<A> as; 
} 
2

¿Qué hay de malo en tener dos mapas, número->> valores, las teclas de la derecha valores-?

+3

Pensé que mantener dos copias de los mismos datos sería más propenso a errores. De todos modos, después de todas las colecciones que he visto, estoy empezando a pensar que es la mejor solución. –

+2

Solo crea un contenedor para los mapas que los mantiene sincronizados. – Stefan

+10

No me gusta el enfoque avalado por esta respuesta. Hay muchas cosas potencialmente incorrectas con esto, incluyendo posiblemente reinventar la rueda, escribir sus propios errores a lo largo del camino, seguridad de la rosca, etc. – bacar

-3

La implementación de Google Guava MultiMap es lo que estoy usando para estos fines.

Map<Key Collection<Values>> 

donde Collection puede ser una ArrayList por ejemplo. Permite mapear múltiples valores almacenados en una colección a una clave. Espero que esto ayude!

+0

No es bidireccional. – Stefan

1

Usando Google guayaba podemos escribir una BiMulitMap primitiva de la siguiente manera.

import java.util.Collection; 

import com.google.common.collect.ArrayListMultimap; 
import com.google.common.collect.Multimap; 

public class BiMultiMap<K,V> { 

    Multimap<K, V> keyToValue = ArrayListMultimap.create(); 
    Multimap<V, K> valueToKey = ArrayListMultimap.create(); 

    public void putForce(K key, V value) { 
     keyToValue.put(key, value); 
     valueToKey.put(value, key); 
    } 

    public void put(K key, V value) { 
     Collection<V> oldValue = keyToValue.get(key); 
     if (oldValue.contains(value) == false) { 
      keyToValue.put(key, value); 
      valueToKey.put(value, key); 
     } 
    } 

    public Collection<V> getValue(K key) { 
     return keyToValue.get(key); 
    } 

    public Collection<K> getKey(V value) { 
     return valueToKey.get(value); 
    } 

    @Override 
    public String toString() { 
     return "BiMultiMap [keyToValue=" + keyToValue + ", valueToKey=" + valueToKey + "]"; 
    } 

} 

Esperamos que ayude a algunas de las necesidades básicas de la bi-direccional Multi mapa. Tenga en cuenta que K y V deben implementar el método hascode y equals correctamente