Przypomnijmy, że mapa to uporządkowane dane składające się z zestawu par klucz-wartość, a każdy klucz może zostać użyty tylko raz na jednej mapie. W tym temacie omówiono 9 podstawowych pytań dotyczących używania Map w Javie i zaimplementowanych w niej klas. Dla uproszczenia w przykładach będę posługiwał się uogólnieniami . Dlatego napiszę po prostu Map, bez podawania specyfikatora Map. Można jednak założyć, że obie wartości K i V są porównywalne, co oznacza, że K rozszerza Porównywalne , a V również rozciąga Porównywalne .
0. Konwersja mapy na listę
W Javie interfejs Map oferuje trzy rodzaje kolekcji: zestaw kluczy, zestaw wartości i zestaw klucz-wartość. Wszystkie z nich można przekształcić w Listę za pomocą konstruktora lub metodyaddAll()
. Poniższy fragment kodu demonstruje, jak utworzyć ArrayList z mapy.
// lista kluczy
List keyList = new ArrayList(Map.keySet());
//lista wartości
List valueList = new ArrayList(Map.valueSet());
//lista klucz-wartość
List entryList = new ArrayList(Map.entrySet());
1. Przejdź przez wszystkie wartości na mapie
Przeglądanie każdej pary klucz-wartość to najbardziej podstawowa procedura przeglądania mapy. W Javie każda para jest przechowywana w polu Map o nazwie Map.Entry .Map.entrySet()
zwraca zestaw par klucz-wartość, więc najskuteczniejszym sposobem iteracji przez wszystkie wartości mapy byłoby:
for(Entry entry: Map.entrySet()) {
//Weź klucz
K key = entry.getKey();
//pobierz wartość
V value = entry.getValue();
}
Możemy również użyć Iterator
, szczególnie w wersjach młodszych niż JDK 1.5
Iterator itr = Map.entrySet().iterator();
while(itr.hasNext()) {
Entry entry = itr.next();
//Weź klucz
K key = entry.getKey();
//pobierz wartość
V value = entry.getValue();
}
2. Zamawianie mapy według kluczy
Organizowanie map według klawiszy to kolejna powszechnie stosowana procedura. Pierwszy sposób polega na dodaniu Map.Entry do listy i sortowaniu przy użyciu komparatora sortującego według wartości.List list = new ArrayList(Map.entrySet());
Collections.sort(list, new Comparator() {
@Override
public int compare(Entry e1, Entry e2) {
return e1.getKey().compareTo(e2.getKey());
}
});
Inny sposób: użyj SortedMap , która dodatkowo porządkuje także swoje klucze. Jednak wszystkie klucze muszą zawierać Porównywalne lub zostać zaakceptowane przez komparator. Jedną z zaimplementowanych klas SortedMap
jest TreeMap . Jego konstruktor akceptuje komparator. Poniższy kod pokazuje, jak zamienić normalny Map
w uporządkowany.
SortedMap sortedMap = new TreeMap(new Comparator() {
@Override
public int compare(K k1, K k2) {
return k1.compareTo(k2);
}
});
sortedMap.putAll(Map);
3. Zamów mapę według wartości
Dodanie mapy do listy, a następnie jej posortowanie działa w tym przypadku, ale tym razem musisz użyćEntry.getValue()
. Poniższy kod jest prawie taki sam jak poprzednio.
List list = new ArrayList(Map.entrySet());
Collections.sort(list, new Comparator() {
@Override
public int compare(Entry e1, Entry e2) {
return e1.getValue().compareTo(e2.getValue());
}
});
Nadal możemy go użyć SortedMap
w tym przypadku, ale tylko wtedy, gdy wartości są unikalne. W takim przypadku możesz zamienić parę klucz-wartość w klucz-wartość. To rozwiązanie ma poważne ograniczenia i nie jest przeze mnie zalecane.
4. Inicjowanie mapy statycznej/niezmiennej
Jeśli chcesz, aby mapa pozostała niezmienna, dobrym sposobem jest skopiowanie jej do niezmiennej mapy. Ta technika programowania defensywnego pomoże Ci stworzyć mapę, która będzie nie tylko bezpieczna w użyciu, ale także bezpieczna dla wątków. Aby zainicjować statyczną/niezmienną mapę, możemy użyć inicjatorastatic
(patrz poniżej). Problem z tym kodem polega na tym, że pomimo zadeklarowania Map jako static final
, nadal możemy z nią pracować po inicjalizacji, na przykład Test.Map.put(3,"three");
. Więc to nie jest prawdziwa niezmienność. Aby utworzyć niezmienną Mapę przy użyciu statycznego inicjatora, potrzebujemy superanonimowej klasy, którą dodamy do niezmiennej Mapy w ostatnim kroku inicjalizacji. Proszę spojrzeć na drugą część kodu. Kiedy zostanie zgłoszony wyjątek UnsupportedOperationException, jeśli uruchomisz Test.Map.put(3,"three");
.
public class Test {
private static final Map Map;
static {
Map = new HashMap();
Map.put(1, "one");
Map.put(2, "two");
}
}
public class Test {
private static final Map Map;
static {
Map aMap = new HashMap();
aMap.put(1, "one");
aMap.put(2, "two");
Map = Collections.unmodifiableMap(aMap);
}
}
Biblioteka Guava obsługuje również różne sposoby inicjowania kolekcji statycznych i niezmiennych. Aby dowiedzieć się więcej o zaletach narzędzia niezmiennych kolekcji Guava, zobacz sekcję Niezmienne kolekcje w poradniku Guava .
5. Różnica pomiędzy HashMap, TreeMap i Hashtable
Istnieją trzy główne implementacje interfejsu Map w Javie: HashMap , TreeMap i Hashtable . Główne różnice są następujące:- Kolejność przejścia . HashMap i HashTable nie gwarantują uporządkowania Mapy; w szczególności nie gwarantują niezmienności kolejności w czasie. Ale
TreeMap
uporządkuje wszystkie wartości w „naturalnej kolejności” kluczy lub według komparatora. - Prawidłowe pary klucz-wartość.
HashMap
pozwala mieć klucz zerowy i wartość zerową.HashTable
nie zezwala na klucz zerowy ani wartość zerową. JeśliTreeMap
zostanie użyty porządek naturalny lub komparator nie pozwoli na klucz zerowy, zostanie zgłoszony wyjątek. - Synchronizacja . Tylko
HashTable
zsynchronizowane, reszta nie. Ale „jeśli implementacja bezpieczna dla wątków nie jest potrzebna, zaleca się użycie ”HashMap
zamiast tegoHashTable
.
. | HashMap | HashTable | TreeMap
-------------------------------------------------------
Упорядочивание |нет |нет | да
null в ключ-oznaczający | да-да | нет-нет | нет-да
синхронизировано | нет | да | нет
производительность | O(1) | O(1) | O(log n)
воплощение | корзины | корзины | красно-чёрное дерево
Przeczytaj więcej o relacji HashMap vs. Mapa Drzewa vs. Hashtable vs. PołączonaHashMapa .
6. Mapa z wyszukiwaniem/widok wsteczny
Czasami potrzebujemy zestawu par klucz-klucz, co oznacza, że wartości są tak samo unikalne jak klucze (wzór jeden do jednego). Ta spójność pozwala na utworzenie na Mapie „odwróconego widoku/wyszukiwania”. Oznacza to, że możemy znaleźć klucz według jego wartości. Ta struktura danych nazywana jest mapą dwukierunkową i niestety nie jest obsługiwana przez JDK. Zarówno Apache Common Collections, jak i Guava oferują dwukierunkowe implementacje map zwane odpowiednio BidiMap i BiMap. Obydwa wprowadzają ograniczenie, które wymusza mapowanie 1:1 pomiędzy kluczami i wartościami.7. Płytka kopia mapy
Prawie wszystkie, jeśli nie wszystkie, mapy w Javie zawierają konstruktor kopiujący dla innej mapy. Ale procedura kopiowania nie jest zsynchronizowana. Oznacza to, że gdy jeden wątek kopiuje Mapę, inny wątek może zmienić jej strukturę. Aby zapobiec nagłej desynchronizacji kopii, należy w takim przypadku skorzystać z jednego z nichCollections.synchronizedMap()
.
Map copiedMap = Collections.synchronizedMap(Map);
Innym interesującym sposobem płytkiego kopiowania jest użycie rozszerzenia clone()
. Ale NIE jest to zalecane nawet przez twórcę frameworku kolekcji Java, Joshuę Blocha. W debacie „ Copy Constructor vs. Cloning ” zajmuje stanowisko: Cytat: „Często włączam metodę publicznego klonowania do konkretnych zajęć, ponieważ ludzie oczekują, że ona tam będzie. ...szkoda, że klonowanie jest zepsute, ale to się wydarzyło. ... Klonowanie to słaby punkt i uważam, że należy ostrzec ludzi o jego ograniczeniach. Z tego powodu nawet nie pokazuję, jak używać metody clone()
do kopiowania Map
8. Utwórz pustą mapę
JeśliMap
niezmienne, użyj:
Map = Collections.emptyMap();
Lub użyj dowolnego innego przykładu wykonania. Na przykład:
Map = new HashMap();
KONIEC
GO TO FULL VERSION