Jeśli mam wartość "foo"i HashMap<String> ftwdla której ftw.containsValue("foo")zwraca true, w jaki sposób mogę uzyskać odpowiedni klucz? Czy muszę przeglądać skrót mapy? Jak najlepiej to zrobić?
Jeśli zdecydujesz się użyć biblioteki Commons Kolekcje zamiast standardowego interfejsu API Java Collections, możesz to zrobić z łatwością.
BidiMap interfejs w bibliotece Collections jest dwukierunkowy map, co pozwala odwzorować klucz do wartości (jak normalnych mapach), a także do mapowania wartości do klucza, co pozwala na wykonywanie wyszukiwań w obu kierunkach. Uzyskanie klucza dla wartości jest obsługiwane przez metodę getKey () .
Jest jednak pewne zastrzeżenie, że mapy bidi nie mogą mieć wielu wartości mapowanych na klucze, a zatem dopóki twój zestaw danych nie ma mapowań 1: 1 między kluczami a wartościami, nie możesz używać bidimaps.
Aktualizacja
Jeśli chcesz polegać na interfejsie API kolekcji Java, musisz zapewnić relację 1: 1 między kluczami i wartościami podczas wstawiania wartości do mapy. Łatwiej to powiedzieć niż zrobić.
Gdy możesz to zapewnić, użyj metody entrySet (), aby uzyskać zestaw wpisów (mapowań) na mapie. Po uzyskaniu zestawu, którego typem jest Map.Entry , iteruj wpisy, porównując przechowywaną wartość z oczekiwaną i uzyskaj odpowiedni klucz .
Aktualizacja nr 2
Wsparcie dla map bidi z lekami generycznymi można znaleźć w Google Guava i refactored bibliotekach Commons-Collections (ta ostatnia nie jest projektem Apache). Dziękujemy Esko za zwrócenie uwagi na brakujące ogólne wsparcie w kolekcjach Apache Commons. Używanie kolekcji z rodzajami sprawia, że kod jest łatwiejszy w utrzymaniu.
... a jeśli lubisz Generics i wszystkie te nowoczesne rzeczy, Google Maps ma BiMap, w którym możesz uzyskać klucz pasujący do określonej wartości, wywołując biMap.inverse (). get (wartość);
Esko
1
Tak, kolekcje Apache Commons nie obsługują generycznych. Istnieją jednak Kolekcje Google, jak już wspomniałeś (których jeszcze nie używam - nie ma jeszcze wersji 1.0), i istnieją ponownie utworzone Kolekcje Commons z obsługą Generics. Znajdziesz to jako projekt Sourceforge @ sourceforge.net/projects/collections
Vineet Reynolds
2
Kolekcje Google nie są refaktoryzowaną wersją zbiorów Commons.
whiskeysierra
12
@whiskeysierra: Nie sądzę, żeby ktokolwiek (obecnie) tak powiedział.
Polecam wymienić .filter(entry -> entry.getValue().equals(value))ze jak nie oświadczenie o zdolności powstał. Ponadto, można wymienić z.filter(entry ->Objects.equals(entry.getValue(), value))null.map(entry -> entry.getKey()).map(Map.Entry::getKey)
Holger
mam problem ze zrozumieniem notacji <T, E> przed Set <T> getKeysByValue () ... jaki jest sens .... inny sposób na zrobienie tego bez korzystania z tego? dzięki
Niektóre dodatkowe informacje ... mogą być dla Ciebie przydatne
Powyższa metoda może nie być dobra, jeśli twoja mapa skrótów jest naprawdę duża. Jeśli twoja mapa skrótów zawiera unikatowy klucz do unikalnego mapowania wartości, możesz zachować jeszcze jeden skrót, który zawiera mapowanie od wartości do klucza.
Oznacza to, że musisz zachować dwie mapy skrótów
1.Key to value
2.Value to key
W takim przypadku możesz użyć drugiej wartości skrótu, aby uzyskać klucz.
Użyj wbudowanej do tego implementacji mapy, takiej jak BiMap z kolekcji Google. Pamiętaj, że kolekcje Google BiMap wymagają unikalnych wartości, a także kluczy, ale zapewnia wysoką wydajność w obu kierunkach
Ręcznie utrzymuj dwie mapy - jedną dla klucza -> wartość, a drugą mapę dla wartości -> klucz
Iteruj przez entrySet()i, aby znaleźć klucze, które pasują do wartości. Jest to najwolniejsza metoda, ponieważ wymaga iteracji przez całą kolekcję, podczas gdy pozostałe dwie metody tego nie wymagają.
To naprawdę nie jest poprawne. Wymaga to nie tylko 1-1, ale także, że zestaw wartości jest rozłączny od zestawu kluczy. Nie można zastosować tego do mapy dwusuwowej {1 -> 2, 2 -> 3}: 2 jest zarówno wartością, jak i kluczem.
Luis A. Florit,
15
Udekoruj mapę własną implementacją
classMyMap<K,V>extendsHashMap<K, V>{Map<V,K> reverseMap =newHashMap<V,K>();@Overridepublic V put(K key, V value){// TODO Auto-generated method stub
reverseMap.put(value, key);returnsuper.put(key, value);}public K getKey(V value){return reverseMap.get(value);}}
Myślę, że jest to interesujące podejście, ale ponieważ relacja musi wynosić 1: 1, pozbyłbym się całkowicie HashMap i zaimplementowałem interfejs Map <K, V>, aby uniknąć duplikatów zarówno wartości, jak i kluczy.
Fran Marzoa,
11
Nie ma jednoznacznej odpowiedzi, ponieważ wiele kluczy można odwzorować na tę samą wartość. Jeśli wymuszasz unikalność za pomocą własnego kodu, najlepszym rozwiązaniem jest utworzenie klasy, która używa dwóch map Hashmap do śledzenia mapowań w obu kierunkach.
Sprytne, ale co, jeśli istnieją 2 lub więcej obiektów KeyValue zawierających tę samą wartość? Który klucz należy wybrać?
Vineet Reynolds,
2
@Vineet, nie rozumiem, w jaki sposób to podejście rozwiązuje pytanie PO. co miałeś na myśli przez „Zatem, kiedy masz wartość, masz również klucz.”?
Qiang Li
9
Myślę, że to najlepsze rozwiązanie, oryginalny adres: Java2s
import java.util.HashMap;import java.util.Map;publicclassMain{publicstaticvoid main(String[] argv){Map<String,String> map =newHashMap<String,String>();
map.put("1","one");
map.put("2","two");
map.put("3","three");
map.put("4","four");System.out.println(getKeyFromValue(map,"three"));}// hm is the map you are trying to get value from itpublicstaticObject getKeyFromValue(Map hm,Object value){for(Object o : hm.keySet()){if(hm.get(o).equals(value)){return o;}}returnnull;}}
Łatwe użycie: jeśli umieścisz wszystkie dane w hasMap i masz item = "Automobile", to szukasz klucza w hashMap. to dobre rozwiązanie.
Tak, właśnie to robi. Ale oczywiście zwraca true, gdy tylko znajdzie jedną wartość, dla której .equals jest prawdziwe, w przeciwieństwie do tego, co OP prawdopodobnie będzie musiał zrobić.
CPerkins
1
Cóż, iteracja wpisów może powrócić z kluczem, gdy tylko znajdzie pasującą wartość. Wiele meczów nie wydawało się problemem.
Jonas K
6
W przypadku interfejsu API do programowania rozwojowego Androida <19, rozwiązanie relacji jeden-do-jednego Vitalii Fedorenko nie działa, ponieważ Objects.equalsnie zostało zaimplementowane. Oto prosta alternatywa:
public<K, V> K getKeyByValue(Map<K, V> map, V value){for(Map.Entry<K, V> entry : map.entrySet()){if(value.equals(entry.getValue())){return entry.getKey();}}returnnull;}
To rozwiązanie działa dla mnie; opracowuję także dla archeologicznej wersji Androida, w moim przypadku, aby klucz znacznika mapy Google był przechowywany na mapie w wydarzeniu „onMarkerClick”. Iterowanie wpisu działa; ale iteracja kluczy i dopasowywanie ich do pozycji za pomocą get () oraz porównywanie danych wyjściowych nie, nie.
Fajnie jest, że chcesz dostarczyć użytecznych rzeczy, ale nie powinna to być odpowiedź „tylko kodowa”, a sam kod nie powinien być pełen zapachów kodu.
Tom
2
Tak, musisz przeglądać skrót, chyba że zaimplementujesz coś zgodnie z sugestiami tych różnych odpowiedzi. Zamiast bawić się z entrySet, po prostu wezmę keySet (), przejdę przez ten zestaw i zatrzymam (pierwszy) klucz, który zapewni ci pasującą wartość. Jeśli potrzebujesz wszystkich kluczy pasujących do tej wartości, oczywiście musisz zrobić wszystko.
Jak sugeruje Jonas, może to już robić metoda zawieraValue, więc możesz po prostu pominąć ten test i wykonać iterację za każdym razem (a może kompilator już wyeliminuje nadmiarowość, kto wie).
Również w stosunku do innych odpowiedzi, jeśli wygląda odwrotna mapa
Map<Value,Set<Key>>
możesz poradzić sobie z nietypowymi mapowaniami klucz> wartość, jeśli potrzebujesz tej możliwości (rozplątując je na bok). To by dobrze włączyło do każdego z rozwiązań, które ludzie sugerują tutaj przy użyciu dwóch map.
Myślę, że odpowiedź można poprawić, dodając wyjaśnienie.
Jonathan
2
-1. Testowałem to z Stringkluczem i wartością. Kiedy dzwonię map.add("1", "2"); map.add("1","3");, mogę dzwonić map.getKey("2");i odbierać "1", nawet jeśli "1"jest to klucz do "3".
jlordo
@Jathanathan ideą tej klasy jest utrzymanie kolejnej mapy HashMap z odwrotnymi mapowaniami, aby oprócz pobierania wartości z klucza można było również pobrać klucz z wartości. Klasy T1 i T2 są nieco mylące; może zamiast tego nazwać je dosłownie kluczem i wartością? Chociaż oczekiwałbym możliwości otrzymania więcej niż jednej wartości lub więcej niż jednego klucza w zamian, w zależności od danych i tego, czego chcesz. Używaj ostrożnie
Chicowitz,
1
@theknightwhosaysni „1” nie jest już kluczem do „2” (już). Jest to również odpowiedź na twoje pytanie, połączenie getValue("1")powróci 3.
jlordo
Przepraszamy jlordo, pomyliłem się co do standardowego zachowania Hashmap: masz rację, że dodanie nowej wartości dla klucza powinno zastąpić starą wartość
Co się stanie, jeśli wiele kluczy ma tę samą wartość?
Cà phê đen
Po przekazaniu wielu kluczy ma tę samą wartość, otrzymamy ostatni klucz jako wynik. przykład: wyjście A 1, B 1, C 1, D 2: jeśli przekażemy wartość 1, wyjście wyniesie C
Amazing India
@AmazingIndia Nie jest to gwarantowane i całkowicie zależy od konkretnej implementacji mapy. Na przykład HashMap nie gwarantuje zamówienia, więc nie masz pojęcia, jakie dane wyjściowe zostaną tu zwrócone.
Niels Doucet,
1
import java.util.HashMap;import java.util.HashSet;import java.util.Set;publicclassValueKeysMap<K, V>extendsHashMap<K,V>{HashMap<V,Set<K>>ValueKeysMap=newHashMap<V,Set<K>>();@Overridepublicboolean containsValue(Object value){returnValueKeysMap.containsKey(value);}@Overridepublic V put(K key, V value){if(containsValue(value)){Set<K> keys =ValueKeysMap.get(value);
keys.add(key);}else{Set<K> keys =newHashSet<K>();
keys.add(key);ValueKeysMap.put(value, keys);}returnsuper.put(key, value);}@Overridepublic V remove(Object key){
V value =super.remove(key);Set<K> keys =ValueKeysMap.get(value);
keys.remove(key);if(keys.size()==0){ValueKeysMap.remove(value);}return value;}publicSet<K> getKeys4ThisValue(V value){Set<K> keys =ValueKeysMap.get(value);return keys;}publicboolean valueContainsThisKey(K key, V value){if(containsValue(value)){Set<K> keys =ValueKeysMap.get(value);return keys.contains(key);}returnfalse;}/*
* Take care of argument constructor and other api's like putAll
*/}
Moje 2 centy. Możesz dostać klucze do tablicy, a następnie przejść przez nią. Wpłynie to na wydajność tego bloku kodu, jeśli mapa jest dość duża, w której najpierw dostajesz klucze do tablicy, co może zająć trochę czasu, a następnie zapętlasz. W przeciwnym razie w przypadku mniejszych map powinno być w porządku.
String[] keys = yourMap.keySet().toArray(newString[0]);for(int i =0; i < keys.length ; i++){//This is your key String key = keys[i];//This is your value
yourMap.get(key)}
map.get(key) == valuenie jest dobrym pomysłem podczas sprawdzania równości obiektów, ponieważ porównujesz odniesienia. Równość obiektów powinna zawsze używać ich.equals()
frododot
1
Chociaż nie odpowiada to bezpośrednio na pytanie, jest powiązane.
W ten sposób nie musisz ciągle tworzyć / iterować. Po prostu stwórz odwrotną mapę raz i zdobądź to, czego potrzebujesz.
/**
* Both key and value types must define equals() and hashCode() for this to work.
* This takes into account that all keys are unique but all values may not be.
*
* @param map
* @param <K>
* @param <V>
* @return
*/publicstatic<K, V>Map<V,List<K>> reverseMap(Map<K,V> map){if(map ==null)returnnull;Map<V,List<K>> reverseMap =newArrayMap<>();for(Map.Entry<K,V> entry : map.entrySet()){
appendValueToMapList(reverseMap, entry.getValue(), entry.getKey());}return reverseMap;}/**
* Takes into account that the list may already have values.
*
* @param map
* @param key
* @param value
* @param <K>
* @param <V>
* @return
*/publicstatic<K, V>Map<K,List<V>> appendValueToMapList(Map<K,List<V>> map, K key, V value){if(map ==null|| key ==null|| value ==null)return map;List<V> list = map.get(key);if(list ==null){List<V> newList =newArrayList<>();
newList.add(value);
map.put(key, newList);}else{
list.add(value);}return map;}
Ważne jest, aby pamiętać, że ponieważ to pytanie, Apache Collections obsługuje ogólne BidiMaps . Dlatego niektóre z najczęściej głosowanych odpowiedzi nie są już dokładne w tym punkcie.
W przypadku serializowanej usługi BidiMap, która obsługuje również zduplikowane wartości (scenariusz jeden do wielu), rozważ także MapDB.org .
public static final String TIME = "time";
iproperties.put(TIME, PbActivityJpa_.time);
Odpowiedzi:
Jeśli zdecydujesz się użyć biblioteki Commons Kolekcje zamiast standardowego interfejsu API Java Collections, możesz to zrobić z łatwością.
BidiMap interfejs w bibliotece Collections jest dwukierunkowy map, co pozwala odwzorować klucz do wartości (jak normalnych mapach), a także do mapowania wartości do klucza, co pozwala na wykonywanie wyszukiwań w obu kierunkach. Uzyskanie klucza dla wartości jest obsługiwane przez metodę getKey () .
Jest jednak pewne zastrzeżenie, że mapy bidi nie mogą mieć wielu wartości mapowanych na klucze, a zatem dopóki twój zestaw danych nie ma mapowań 1: 1 między kluczami a wartościami, nie możesz używać bidimaps.
Aktualizacja
Jeśli chcesz polegać na interfejsie API kolekcji Java, musisz zapewnić relację 1: 1 między kluczami i wartościami podczas wstawiania wartości do mapy. Łatwiej to powiedzieć niż zrobić.
Gdy możesz to zapewnić, użyj metody entrySet (), aby uzyskać zestaw wpisów (mapowań) na mapie. Po uzyskaniu zestawu, którego typem jest Map.Entry , iteruj wpisy, porównując przechowywaną wartość z oczekiwaną i uzyskaj odpowiedni klucz .
Aktualizacja nr 2
Wsparcie dla map bidi z lekami generycznymi można znaleźć w Google Guava i refactored bibliotekach Commons-Collections (ta ostatnia nie jest projektem Apache). Dziękujemy Esko za zwrócenie uwagi na brakujące ogólne wsparcie w kolekcjach Apache Commons. Używanie kolekcji z rodzajami sprawia, że kod jest łatwiejszy w utrzymaniu.
źródło
Jeśli struktura danych ma mapowanie wiele do jednego między kluczami i wartościami, należy iterować wpisy i wybierać wszystkie odpowiednie klucze:
W przypadku relacji jeden do jednego możesz zwrócić pierwszy dopasowany klucz:
W Javie 8:
Ponadto, dla użytkowników Guava, BiMap może być przydatny. Na przykład:
źródło
o(1)
. Jeśli iterujesz po wartościach, zabije to wydajność. Jeśli chceszbetter performance
i maone-one
związek, możesz użyćanother map
gdzievalue is a key
.filter(entry -> entry.getValue().equals(value))
ze jak nie oświadczenie o zdolności powstał. Ponadto, można wymienić z.filter(entry ->
Objects.equals
(entry.getValue(), value))
null
.map(entry -> entry.getKey())
.map(Map.Entry::getKey)
Niektóre dodatkowe informacje ... mogą być dla Ciebie przydatne
Powyższa metoda może nie być dobra, jeśli twoja mapa skrótów jest naprawdę duża. Jeśli twoja mapa skrótów zawiera unikatowy klucz do unikalnego mapowania wartości, możesz zachować jeszcze jeden skrót, który zawiera mapowanie od wartości do klucza.
Oznacza to, że musisz zachować dwie mapy skrótów
W takim przypadku możesz użyć drugiej wartości skrótu, aby uzyskać klucz.
źródło
Myślę, że twoje wybory są
entrySet()
i, aby znaleźć klucze, które pasują do wartości. Jest to najwolniejsza metoda, ponieważ wymaga iteracji przez całą kolekcję, podczas gdy pozostałe dwie metody tego nie wymagają.źródło
Możesz wstawić zarówno klucz, parę wartości, jak i jej odwrotność do struktury mapy
Użycie map.get („theValue”) zwróci następnie „theKey”.
To szybki i brudny sposób, w jaki tworzyłem stałe mapy, które będą działać tylko dla wybranych kilku zestawów danych:
źródło
Udekoruj mapę własną implementacją
źródło
Nie ma jednoznacznej odpowiedzi, ponieważ wiele kluczy można odwzorować na tę samą wartość. Jeśli wymuszasz unikalność za pomocą własnego kodu, najlepszym rozwiązaniem jest utworzenie klasy, która używa dwóch map Hashmap do śledzenia mapowań w obu kierunkach.
źródło
Aby znaleźć wszystkie klucze, które odwzorowują tę wartość, iteruj wszystkie pary w haszapie, używając
map.entrySet()
.źródło
Za pomocą Java 8:
źródło
value=="foo"
to nie zadziała.equals
należy używać do porównywania ciągów.value
że została internowana.Jeśli budujesz mapę we własnym kodzie, spróbuj połączyć klucz i wartość na mapie razem:
Następnie, gdy masz wartość, masz również klucz.
źródło
Myślę, że to najlepsze rozwiązanie, oryginalny adres: Java2s
Łatwe użycie: jeśli umieścisz wszystkie dane w hasMap i masz item = "Automobile", to szukasz klucza w hashMap. to dobre rozwiązanie.
źródło
Obawiam się, że będziesz musiał po prostu powtórzyć swoją mapę. Najkrócej mogłem wymyślić:
źródło
źródło
Wydaje się, że najlepszym sposobem jest iteracja wpisów,
map.entrySet()
ponieważmap.containsValue()
prawdopodobnie i tak to robi.źródło
W przypadku interfejsu API do programowania rozwojowego Androida <19, rozwiązanie relacji jeden-do-jednego Vitalii Fedorenko nie działa, ponieważ
Objects.equals
nie zostało zaimplementowane. Oto prosta alternatywa:źródło
Możesz skorzystać z poniższych:
źródło
Tak, musisz przeglądać skrót, chyba że zaimplementujesz coś zgodnie z sugestiami tych różnych odpowiedzi. Zamiast bawić się z entrySet, po prostu wezmę keySet (), przejdę przez ten zestaw i zatrzymam (pierwszy) klucz, który zapewni ci pasującą wartość. Jeśli potrzebujesz wszystkich kluczy pasujących do tej wartości, oczywiście musisz zrobić wszystko.
Jak sugeruje Jonas, może to już robić metoda zawieraValue, więc możesz po prostu pominąć ten test i wykonać iterację za każdym razem (a może kompilator już wyeliminuje nadmiarowość, kto wie).
Również w stosunku do innych odpowiedzi, jeśli wygląda odwrotna mapa
możesz poradzić sobie z nietypowymi mapowaniami klucz> wartość, jeśli potrzebujesz tej możliwości (rozplątując je na bok). To by dobrze włączyło do każdego z rozwiązań, które ludzie sugerują tutaj przy użyciu dwóch map.
źródło
Możesz uzyskać klucz, używając wartości, używając następującego kodu ...
źródło
źródło
String
kluczem i wartością. Kiedy dzwonięmap.add("1", "2"); map.add("1","3");
, mogę dzwonićmap.getKey("2");
i odbierać"1"
, nawet jeśli"1"
jest to klucz do"3"
.getValue("1")
powróci3
.W java8
źródło
źródło
źródło
źródło
źródło
Użyj cienkiego opakowania: HMap
źródło
Moje 2 centy. Możesz dostać klucze do tablicy, a następnie przejść przez nią. Wpłynie to na wydajność tego bloku kodu, jeśli mapa jest dość duża, w której najpierw dostajesz klucze do tablicy, co może zająć trochę czasu, a następnie zapętlasz. W przeciwnym razie w przypadku mniejszych map powinno być w porządku.
źródło
Myślę, że keySet () może dobrze znaleźć klucze mapowane na wartość i mieć lepszy styl kodowania niż entrySet () .
Dawny:
Załóżmy, że masz mapę HashMap , ArrayList res , wartość, dla której chcesz znaleźć wszystkie mapowanie kluczy , a następnie przechowuj klucze w res .
Możesz napisać kod poniżej:
zamiast korzystać z entrySet () poniżej:
Mam nadzieję, że to pomoże :)
źródło
map.get(key) == value
nie jest dobrym pomysłem podczas sprawdzania równości obiektów, ponieważ porównujesz odniesienia. Równość obiektów powinna zawsze używać ich.equals()
Chociaż nie odpowiada to bezpośrednio na pytanie, jest powiązane.
W ten sposób nie musisz ciągle tworzyć / iterować. Po prostu stwórz odwrotną mapę raz i zdobądź to, czego potrzebujesz.
źródło
Ważne jest, aby pamiętać, że ponieważ to pytanie, Apache Collections obsługuje ogólne BidiMaps . Dlatego niektóre z najczęściej głosowanych odpowiedzi nie są już dokładne w tym punkcie.
W przypadku serializowanej usługi BidiMap, która obsługuje również zduplikowane wartości (scenariusz jeden do wielu), rozważ także MapDB.org .
źródło
Jeśli chcesz uzyskać klucz od wartości, najlepiej użyć bidimapy (mapy dwukierunkowe), możesz uzyskać klucz od wartości w czasie O (1).
Wadą tego rozwiązania jest to, że można używać tylko unikatowego zestawu kluczy i zestawu wartości.
W Javie istnieje struktura danych o nazwie Tabela , która jest niczym innym jak mapą podobnych map
Tabela <A, B, C> == mapa <A, mapa <B, C>>
Tutaj można uzyskać
map<B,C>
, wysyłając zapytanieT.row(a);
, a także można uzyskaćmap<A,C>
, wysyłając zapytanieT.column(b);
W twoim specjalnym przypadku wstaw C jako pewną stałą.
Czyli jak <a1, b1, 1> <a2, b2, 1>, ...
Więc jeśli znajdziesz poprzez T.row (a1) ---> zwraca mapę z -> pobierz zestaw kluczy to zwrócona mapa.
Jeśli musisz znaleźć wartość klucza, T.column (b2) -> zwraca mapę z -> pobierz zestaw kluczy z zwróconej mapy.
Zalety w stosunku do poprzedniego przypadku:
źródło