Mapa przechowuje pary klucz-wartość
HashMap (z java.util) przechowuje powiązania: każdy klucz wskazuje jedną wartość, a wartości wyszukujesz po kluczu w mniej więcej stałym czasie. Pomyśl o słowniku: słowo jest kluczem, a definicja wartością.
Dwa parametry typu to <KeyType, ValueType>. Tutaj klucze to String, a wartości to Integer. Podobnie jak przy ArrayList zmienną zwykle deklaruje się jako interfejs Map, a tworzy HashMap.
put, get i nadpisywanie
Dwie rzeczy warto zapamiętać:
- Klucz jest unikalny.
putz istniejącym kluczem zastępuje jego wartość i zwraca starą. getdla brakującego klucza zwracanull, a nie błąd. Automatyczne rozpakowanie tegonulldointrzucaNullPointerException, co jest częstym źródłem błędów.
getOrDefault omija pułapkę null
Zamiast za każdym razem sprawdzać null, poproś o wartość zapasową:
To najczystszy sposób obsługi wyszukiwań typu „może istnieje” i prowadzi prosto do najsłynniejszego wzorca z HashMap.
Liczenie wystąpień
Liczenie, ile razy pojawia się każdy element, to podręcznikowe zadanie dla HashMap:
Wzorzec map.put(key, map.getOrDefault(key, 0) + 1) czyta się jako „weź bieżącą liczbę (albo zero), dodaj jeden i zapisz z powrotem”. Zgrabniejszy odpowiednik to counts.merge(word, 1, Integer::sum).
Sprawdzanie i usuwanie
putIfAbsent(key, value) zapisuje wartość tylko wtedy, gdy klucza brakuje, co przydaje się przy leniwej inicjalizacji.
Pętla po HashMap
Najczęstsza pętla przechodzi po entrySet() i daje każdy klucz razem z wartością:
Jeśli potrzebujesz tylko kluczy albo tylko wartości:
Możesz też użyć forEach z lambdą: ages.forEach((name, age) -> System.out.println(name + ": " + age));.
HashMap nie zachowuje kolejności
HashMap nie gwarantuje żadnej kolejności iteracji: wynika ona z haszowania i może się zmieniać między uruchomieniami. Jeśli potrzebujesz przewidywalnej kolejności:
LinkedHashMapzachowuje kolejność wstawiania.TreeMaputrzymuje klucze posortowane według porządku naturalnego (albo podanego przez ciebieComparator).
Wszystkie trzy implementują interfejs Map, więc zamiana to zmiana jednego wiersza z konstruktorem.
Klucze muszą być haszowalne
HashMap znajduje wpisy przez haszowanie klucza, więc hashCode() i equals() klucza muszą być ze sobą zgodne. Typy wbudowane, takie jak String i Integer, robią to poprawnie. Jeśli jako klucza używasz własnej klasy, nadpisz zarówno equals, jak i hashCode. Inaczej dwa obiekty „równe” znaczeniowo trafią do różnych kubełków, a wyszukiwania będą zagadkowo zawodzić.
Dalej: HashSet
HashMap odpowiada na pytanie „jaka wartość jest zapisana pod tym kluczem?”. Gdy interesuje cię tylko to, czy coś w ogóle występuje, czyli zbiór unikalnych wartości bez powiązanych danych, narzędziem jest HashSet, omówiony na następnej stronie.
Najczęściej zadawane pytania
Jak utworzyć HashMap w Javie?
Zadeklaruj ją z dwoma parametrami typu, czyli typem klucza i typem wartości, i wywołaj konstruktor: Map<String, Integer> ages = new HashMap<>();. Potem dodawaj wpisy przez ages.put("Ada", 36); i odczytuj je przez ages.get("Ada");. Zaimportuj java.util.HashMap i java.util.Map.
Jak przejść pętlą po HashMap w Javie?
Przejdź pętlą for-each po map.entrySet(), aby dostać każdy klucz razem z wartością: for (Map.Entry<String, Integer> e : map.entrySet()) { ... }, odczytując e.getKey() i e.getValue(). Możesz też iterować po map.keySet(), gdy potrzebujesz tylko kluczy, albo po map.values(), gdy potrzebujesz tylko wartości. Pamiętaj, że HashMap nie zachowuje kolejności wstawiania.
Czym różni się get od getOrDefault?
get(key) zwraca wartość dla klucza albo null, jeśli klucza nie ma, co może prowadzić do NullPointerException, gdy od razu użyjesz wyniku. getOrDefault(key, fallback) zwraca wartość, jeśli istnieje, a w przeciwnym razie przekazaną wartość zapasową, więc unikasz sprawdzania null. Szczególnie przydaje się to przy liczeniu: counts.put(c, counts.getOrDefault(c, 0) + 1).