2011-11-09 22 views
19

Szukam sposobu na przechowywanie par klucz-wartość. Potrzebuję wyszukiwania dwukierunkowego, ale jednocześnie muszę przechowywać wiele wartości dla tego samego klucza. Innymi słowy, coś jak BidiMap, ale dla każdego klucza może istnieć wiele wartości. Na przykład musi mieć możliwość przechowywania par takich jak: "s1" -> 1, "s2" -> 1, "s3" -> 2, i muszę mieć możliwość uzyskania wartości odwzorowanej na każdy klawisz, oraz dla każdej wartości uzyskaj wszystkie klucze z nią powiązane.Dwukierunkowa wielowartościowa mapa w języku Java

+3

Mówisz o potrzebie posiadania wielu wartości na klucz, ale w twoim przykładzie nie masz klucza z wieloma wartościami, ale jedną wartość z dwoma kluczami. Powinieneś to wyjaśnić. Jeśli twój przykład pasuje do twojego pytania, dostaniesz lepsze odpowiedzi ;-) – pushy

+0

http://www.jguru.com/faq/view.jsp?EID=1317828 tutaj możesz dowiedzieć się jak stworzyć multimap – maks

+0

@pushy, ten sam problem, jeśli odwrócę mapę i zachowam liczby całkowite jako klucze zamiast wartości, otrzymuję mapowanie jeden-do-wielu. W każdym razie, dzięki za poprawkę. :) –

Odpowiedz

19

Potrzebujesz pomocy w relacjach między wieloma osobami? Najbliżej można uzyskać Guava 's Multimap jak @Mechkov napisał - ale bardziej szczegółowo Multimap połączenie z Multimaps.invertFrom. "BiMultimap" nie jest jeszcze zaimplementowany, ale jest an issue, prosząc o tę funkcję w bibliotece Google Guava.

W tym momencie masz kilka opcji:

  1. Jeśli twój „BiMultimap” będzie niezmienną stałą - użyj Multimaps.invertFrom i ImmutableMultimap/ImmutableListMultimap/ImmutableSetMultimap (każdy z theese trzech ma różne wartości zbiór przechowywanie). Niektóre kod (przykład wzięty z aplikacją I rozwijać, wykorzystuje Enum s i 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. Jeśli naprawdę chcesz, żeby Twój Multimap być modyfikowane, to będzie trudne do utrzymania zarówno K-> V i V-> K wariantów, chyba że będziesz modyfikować tylko kToVMultimap i zadzwonić pod numer invertFrom za każdym razem, gdy chcesz mieć jego odwróconą kopię (i uczynienie tej kopii niezmodyfikowaną, aby upewnić się, że przypadkiem nie zmienisz vToKMultimap, co nie zaktualizowałoby kToVMultimap). Nie jest to optymalne, ale powinno być w tym przypadku.

  3. (Nie twoja sprawa prawdopodobnie wymienione jako bonus): BiMap interfejs i klasy wykonawcze ma .inverse() metodę, która daje BiMap<V, K> widok z BiMap<K, V> i sam po biMap.inverse().inverse(). Jeśli wspomniałem wcześniej, to prawdopodobnie będzie miało coś podobnego.

  4. (Edycja październik 2016) Można również użyć new graph API który będzie obecny w Guava 20:

    Jako całość, wspólny.Wykres obsługuje wykresy z następujących odmian:

    • skierowane wykresy
    • nieukierunkowane wykresy
    • węzłów i/lub krawędzie wartości powiązanych (wag, etykiety itd)
    • wykresy do/nie pozwala siebie pętle
    • wykresy do/nie pozwalają na równoległe krawędzie (wykresy z równoległymi krawędziami są czasami nazywane multigraphs)
    • wykresy, których krawędzie są węzły/wstawiania uporządkowane, sortowane lub nieuporządkowane
    • 0.123.
-1

Nadzieja mam prawym

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

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

Co jest złego w posiadanie dwóch mapach, kluczykowy> wartości, values-> klucze?

+3

Myślałem, że zachowanie dwóch kopii tych samych danych będzie bardziej podatne na błędy. W każdym razie, po wszystkich kolekcjach, które oglądałem, zaczynam myśleć, że to najlepsze rozwiązanie. –

+2

Po prostu utwórz opakowanie dla map, które je zsynchronizują. – Stefan

+10

Nie podoba mi się podejście potwierdzone tą odpowiedzią. Jest wiele rzeczy, które mogą być z tym nie tak, włącznie z możliwością ponownego odkrywania koła, pisania własnych błędów po drodze, bezpieczeństwa wątków itp. – bacar

-3

Google Guava MultiMap implementacja jest tym, czego używam do tych celów.

Map<Key Collection<Values>> 

gdzie Kolekcja może być na przykład ArrayList. Pozwala na mapowanie wielu wartości przechowywanych w kolekcji na klucz. Mam nadzieję, że to pomoże!

+0

Nie dwukierunkowe. – Stefan

1

Korzystanie z Google Guava możemy napisać prymitywną BiMulitMap jak poniżej.

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

} 

Mam nadzieję, że pomoże to w niektórych podstawowych potrzebach dwukierunkowej wieloplanowej mapy. Zauważ, że K i V muszą implementować hascode i jest to metoda równa