2011-11-09 18 views
19

Sto cercando un modo per memorizzare coppie chiave-valore. Ho bisogno che la ricerca sia bidirezionale, ma allo stesso tempo ho bisogno di memorizzare più valori per la stessa chiave. In altre parole, qualcosa come una BidiMap, ma per ogni chiave ci possono essere più valori. Ad esempio, deve essere in grado di contenere coppie come: "s1" -> 1, "s2" -> 1, "s3" -> 2, e ho bisogno di essere in grado di ottenere il valore mappato a ciascuna chiave, e per ogni valore, prendi tutte le chiavi ad esso associate.Mappa multivalore bidirezionale in Java

+3

Si parla della necessità di avere più valori per chiave, ma nell'esempio non si ha alcuna chiave con più valori, ma un valore con due chiavi. Probabilmente dovresti chiarirlo. Se il tuo esempio si adatta alla tua domanda otterrai risposte migliori ;-) – pushy

+0

http://www.jguru.com/faq/view.jsp?EID=1317828 qui puoi trovare come creare multimap – maks

+0

@pushy, stesso problema, se inverto la mappa e mantengo gli interi come chiavi anziché come valori, ottengo una mappatura uno-a-molti. Comunque, grazie per la correzione. :) –

risposta

19

Quindi hai bisogno di supporto per le relazioni molti-a-molti? Il minimo che si possa ottenere èMultimap come ha scritto @Mechkov - ma in particolare la combinazione Multimap con Multimaps.invertFrom. "BiMultimap" non è ancora implementato, ma c'è an issue che richiede questa funzione nella libreria Google Guava.

A questo punto si hanno poche opzioni:

  1. Se il "BiMultimap" sta per costante immutabile - utilizzare Multimaps.invertFrom e ImmutableMultimap/ImmutableListMultimap/ImmutableSetMultimap (ciascuno dei theese tre ha diversi valori di raccolta memorizzazione). Alcuni di codice (esempio tratto dalla app io sviluppo, utilizza Enum s e 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. Se si vuole veramente il tuo Multimap per essere modificabili, sarà difficile mantenere sia K-> V e V-> Varianti K a meno che non si modifichi solo kToVMultimap e si chiami invertFrom ogni volta che si desidera avere la copia invertita (e tale copia non è modificabile per garantire che non si modifichi accidentalmentecosa non si aggiorna kToVMultimap). Questo non è ottimale, ma dovrebbe essere fatto in questo caso.

  3. (Non è il caso, probabilmente, citato come bonus): BiMap l'interfaccia e le classi che implementano ha .inverse() metodo che dà BiMap<V, K> vista da BiMap<K, V> e se stesso dopo biMap.inverse().inverse(). Se this issue ho menzionato prima, probabilmente avrà qualcosa di simile.

  4. (EDIT ottobre 2016) È inoltre possibile utilizzare new graph API che sarà presente in Guava 20:

    Nel suo complesso, comune.grafico supporta grafici delle seguenti varietà:

    • grafi orientati
    • grafi non orientati
    • nodi e/o bordi con i valori associati (pesi, etichette, ecc)
    • grafici che fanno/non fare consentire self-loop
    • grafici che fanno/non consentono bordi paralleli (grafici con bordi paralleli vengono talvolta chiamati multigrafi)
    • grafici i cui nodi/bordi sono inserimento ordinato, ordinato o non ordinato
    • 0.123.
-1

Speranza I got you giusti

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

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

Cosa c'è di sbagliato con avere due mappe, KEY-> valori, chiavi VALORI>?

+3

Ho pensato che tenere due copie degli stessi dati sarebbe più soggetto a errori. Comunque, dopo tutte le collezioni che ho visto, sto iniziando a pensare che sia la soluzione migliore. –

+2

Basta creare un wrapper per le mappe che li mantiene sincronizzati. – Stefan

+10

Non mi piace l'approccio approvato da questa risposta. Ci sono molte cose potenzialmente sbagliate in questo, tra cui forse reinventare la ruota, scrivere i propri bug lungo il percorso, sicurezza dei thread, ecc. – bacar

-3

L'implementazione di Google Guava MultiMap è ciò che sto utilizzando per questi scopi.

Map<Key Collection<Values>> 

dove Collection può essere un ArrayList ad esempio. Consente a più valori memorizzati in una raccolta di essere mappati su una chiave. Spero che questo aiuti!

+0

Non bidirezionale. – Stefan

1

Utilizzo di Google Guava possiamo scrivere un BiMulitMap primitivo come di seguito.

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 + "]"; 
    } 

} 

Spero che questo aiuti alcune esigenze di base della Multi mappa bidirezionale. Nota che K e V devono implementare correttamente il metodo hascode e uguali