Jak zainicjowałbyś statyczny kod Map
w Javie?
Metoda pierwsza: inicjalizator statyczny
Metoda druga: inicjator instancji (anonimowa podklasa) lub inna metoda?
Jakie są zalety i wady każdego z nich?
Oto przykład ilustrujący dwie metody:
import java.util.HashMap;
import java.util.Map;
public class Test {
private static final Map<Integer, String> myMap = new HashMap<>();
static {
myMap.put(1, "one");
myMap.put(2, "two");
}
private static final Map<Integer, String> myMap2 = new HashMap<>(){
{
put(1, "one");
put(2, "two");
}
};
}
Map.of
innegoMap.ofEntries
, sprawdź stackoverflow.com/a/37384773/1216775Odpowiedzi:
Inicjator wystąpienia jest w tym przypadku tylko cukrem syntaktycznym, prawda? Nie rozumiem, dlaczego potrzebujesz dodatkowej anonimowej klasy tylko do inicjalizacji. I to nie zadziała, jeśli tworzona klasa jest ostateczna.
Możesz stworzyć niezmienną mapę za pomocą statycznego inicjalizatora:
źródło
Podoba mi się sposób Guawy inicjowania statycznej, niezmiennej mapy:
Jak widać, jest bardzo zwięzły (ze względu na wygodne metody fabryczne w
ImmutableMap
).Jeśli chcesz, aby mapa zawierała więcej niż 5 wpisów, nie możesz już jej używać
ImmutableMap.of()
. Zamiast tego spróbuj wykonaćImmutableMap.builder()
następujące czynności:Aby dowiedzieć się więcej o zaletach niezmiennych narzędzi do gromadzenia Guava, zobacz Immutable Collections wyjaśnione w Guava User Guide .
(Podzbiór) Guawa nazywała się kolekcjami Google . Jeśli nie korzystasz jeszcze z tej biblioteki w swoim projekcie Java, zdecydowanie zalecamy jej wypróbowanie! Guava szybko stała się jedną z najpopularniejszych i najbardziej przydatnych darmowych bibliotek stron trzecich dla Javy, jak zgadzają się inni użytkownicy SO . (Jeśli dopiero zaczynasz przygodę, za tym linkiem kryją się doskonałe zasoby edukacyjne).
Aktualizacja (2015) : Jeśli chodzi o Javę 8 , nadal używałbym podejścia Guava, ponieważ jest znacznie czystsze niż cokolwiek innego. Jeśli nie chcesz zależności Guava, rozważ prostą, starą metodę init . Hack z dwuwymiarową tablicą i Stream API jest dość brzydki, jeśli mnie zapytasz, i staje się brzydszy, jeśli musisz utworzyć mapę, której klucze i wartości nie są tego samego typu (jak
Map<Integer, String>
w pytaniu).Jeśli chodzi o przyszłość Guavy w ogóle, w odniesieniu do Java 8, Louis Wasserman powiedział to w 2014 roku, a [ aktualizacja ] w 2016 roku ogłoszono, że Guava 21 będzie wymagał i poprawnie obsługiwał Javę 8 .
Aktualizacja (2016) : jak zauważa Tagir Valeev , Java 9 w końcu sprawi, że będzie to łatwe , używając wyłącznie JDK, dodając metody fabryczne wygody dla kolekcji:
źródło
Użyłbym:
źródło
Java 5 zapewnia bardziej zwartą składnię:
źródło
HashMap implements Serializable
. Ponieważ faktycznie tworzysz podklasę HashMap za pomocą tej „sztuczki”, domyślnie tworzysz klasę możliwą do serializacji. I do tego należy podać serialUID.Double brace initialization can cause memory leaks when used from a non-static context, because the anonymous class created will maintain a reference to the surrounding object. It has worse performance than regular initialization because of the additional class loading required. It can cause equals() comparisons to fail, if the equals() method does not accept subclasses as parameter. And finally, pre Java 9 it cannot be combined with the diamond operator, because that cannot be used with anonymous classes.
- IntelliJHashMap.equals
jest zdefiniowany w dowolnej podklasie mapyAbstractMap
i działa na niej , więc nie stanowi to problemu. Operator diamentów jest denerwujący, ale jak już wspomniano, został już rozwiązany.Zaletą drugiej metody jest to, że można ją owinąć,
Collections.unmodifiableMap()
aby zagwarantować, że nic nie będzie później aktualizować kolekcji:źródło
Oto jednowierszowy inicjalizator mapy statycznej Java 8:
Edycja: aby zainicjować
Map<Integer, String>
jak w pytaniu, potrzebujesz czegoś takiego:Edycja (2): i_am_zero oferuje lepszą, kompatybilną z typem wersję, która wykorzystuje strumień
new SimpleEntry<>(k, v)
wywołań. Sprawdź tę odpowiedź: https://stackoverflow.com/a/37384773/3950982źródło
String[][]
nie zrobię tego,Object[][]
jest potrzebne). IMHO, takie podejście jest brzydkie (tym bardziej z obsadami) i trudne do zapamiętania; nie użyłbym tego sam.Map.of
w Javie 9+Szczegóły w JEP 269 . JDK 9 osiągnął ogólną dostępność we wrześniu 2017 r.
źródło
Map.ofEntries
Java 9
Możemy użyć
Map.ofEntries
, dzwoniąc,Map.entry( k , v )
aby utworzyć każdy wpis.Możemy również użyć,
Map.of
jak sugeruje Tagir w swojej odpowiedzi tutaj, ale nie możemy użyć więcej niż 10 wpisówMap.of
.Java 8 (Neat Solution)
Możemy stworzyć Strumień wpisów na mapie. Mamy już dwie implementacje,
Entry
wjava.util.AbstractMap
których SimpleEntry i SimpleImmutableEntry . W tym przykładzie możemy użyć byłego jako:źródło
new SimpleEntry<>()
droga jest znacznie mniej czytelna niż statycznaput()
: /W przypadku kolekcji Eclipse działają wszystkie następujące elementy:
Możesz także statycznie inicjować prymitywne mapy za pomocą Eclipse Collections.
Uwaga: jestem osobą odpowiedzialną za kolekcje Eclipse
źródło
W tej sytuacji nigdy nie stworzyłbym anonimowej podklasy. Inicjatory statyczne działają równie dobrze, jeśli na przykład chcesz uniemożliwić modyfikację mapy:
źródło
Może warto sprawdzić Kolekcje Google , np. Filmy, które mają na swojej stronie. Zapewniają różne sposoby inicjowania map i zestawów, a także niezmienne kolekcje.
Aktualizacja: ta biblioteka nosi teraz nazwę Guava .
źródło
Lubię anonimowe zajęcia, ponieważ łatwo sobie z tym poradzić:
źródło
Jeśli zadeklarujemy więcej niż jedną stałą, kod ten zostanie zapisany w bloku statycznym i będzie to trudne do utrzymania w przyszłości. Lepiej więc użyć anonimowej klasy.
I sugeruje się stosowanie niemodyfikowalnej mapy dla stałych, w przeciwnym razie nie można jej traktować jako stałej.
źródło
Mógłbym zdecydowanie zasugerować styl „podwójnej inicjacji nawiasów klamrowych” zamiast stylu bloku statycznego.
Ktoś może komentować, że nie lubi anonimowych zajęć, kosztów ogólnych, wydajności itp.
Ale bardziej uważam to za czytelność kodu i łatwość konserwacji. Z tego punktu widzenia podwójny nawias klamrowy to lepszy styl kodu niż metoda statyczna.
Ponadto, jeśli znasz GC anonimowej klasy, zawsze możesz przekonwertować ją na normalną HashMap za pomocą
new HashMap(Map map)
.Możesz to zrobić, dopóki nie napotkasz innego problemu. Jeśli to zrobisz, powinieneś zastosować dla niego kompletny inny styl kodowania (np. Brak statycznej, fabrycznej klasy).
źródło
Jak zwykle apache-commons ma odpowiednią metodę MapUtils.putAll (Map, Object []) :
Na przykład, aby utworzyć mapę kolorów:
źródło
Arrays.asMap( ... )
w zwykłej Javie myślę, że to najlepsze rozwiązanie. Nowe odkrywanie koła jest zwykle głupie. Bardzo niewielkim minusem jest to, że w przypadku generyków będzie wymagał niezaznaczonej konwersji.SuppressWarnings( unchecked )
w Eclipse z linią podobną doMap<String, String> dummy = MapUtils.putAll(new HashMap<String, String>(), new Object[][]... )
String[][]
, dostaję „ostrzeżenie”! I oczywiście, że działa tylko jeśliK
iV
są tej samej klasy. Rozumiem, że nie ustawiłeś (co zrozumiałe) „niezaznaczonej konwersji” na „Ignoruj” w konfiguracji Eclipse?Oto moje ulubione, gdy nie chcę (lub nie mogę) używać Guawy
ImmutableMap.of()
lub jeśli potrzebuję mutableMap
:Jest bardzo kompaktowy i ignoruje zbłąkane wartości (tzn. Ostatni klucz bez wartości).
Stosowanie:
źródło
Jeśli chcesz niemodyfikowalne mapę, wreszcie java 9 dodaje metodę chłodny fabryki
of
doMap
interfejsu. Podobną metodę dodano również do Set, List.Map<String, String> unmodifiableMap = Map.of("key1", "value1", "key2", "value2");
źródło
Wolę używać statycznego inicjalizatora, aby uniknąć generowania anonimowych klas (co nie miałoby żadnego dalszego celu), dlatego wymienię wskazówki dotyczące inicjowania przy użyciu statycznego inicjatora. Wszystkie wymienione rozwiązania / wskazówki są bezpieczne dla typu.
Uwaga: pytanie nie mówi nic o uczynieniu mapy niemodyfikowalną, więc pominę to, ale wiem, że można to łatwo zrobić
Collections.unmodifiableMap(map)
.Pierwsza wskazówka
Pierwsza wskazówka jest taka, że możesz zrobić lokalne odniesienie do mapy i nadać jej KRÓTKĄ nazwę:
Druga wskazówka
Druga wskazówka jest taka, że możesz utworzyć metodę pomocnika, aby dodać wpisy; możesz również upublicznić tę metodę pomocniczą, jeśli chcesz:
Metoda pomocnicza tutaj nie nadaje się do ponownego użycia, ponieważ może tylko dodawać elementy
myMap2
. Aby uczynić go ponownie użytecznym, moglibyśmy uczynić samą mapę parametrem metody pomocniczej, ale wtedy kod inicjujący nie byłby krótszy.Trzecia wskazówka
Trzecia wskazówka polega na tym, że możesz stworzyć klasę pomocniczą podobną do konstruktora z wypełniającą funkcjonalnością. To naprawdę prosta, 10-liniowa klasa pomocnicza, która jest bezpieczna dla typu:
źródło
Anonimowa klasa, którą tworzysz, działa dobrze. Należy jednak pamiętać, że jest to klasa wewnętrzna i jako taka będzie zawierać odwołanie do instancji klasy otaczającej. Przekonasz się więc, że nie możesz z nim zrobić pewnych rzeczy (używając XStream dla jednego). Otrzymasz bardzo dziwne błędy.
To powiedziawszy, dopóki jesteś świadomy, to podejście jest w porządku. Używam go przez większość czasu do zwięzłego inicjowania wszelkiego rodzaju kolekcji.
EDYCJA: Poprawnie wskazano w komentarzach, że jest to klasa statyczna. Oczywiście nie przeczytałem tego wystarczająco dokładnie. Jednak moje komentarze mają nadal zastosowanie do anonimowych klas wewnętrznych.
źródło
Jeśli chcesz czegoś zwięzłego i względnie bezpiecznego, możesz po prostu zmienić sprawdzanie typu kompilacji na czas wykonywania:
Ta implementacja powinna wyłapać wszelkie błędy:
źródło
W Javie 8 zacząłem używać następującego wzorca:
To nie jest najbardziej zwięzłe i trochę ronda, ale
java.util
źródło
toMap
podpisu, w tym dostawcy map, aby określić typ mapy.Jeśli potrzebujesz tylko dodać jedną wartość do mapy, możesz użyć Collections.singletonMap :
źródło
Możesz używać
StickyMap
iMapEntry
od Cactoos :źródło
Twoje drugie podejście (inicjalizacja podwójnego nawiasu klamrowego) jest uważane za anty-wzór , więc wybrałbym pierwsze podejście.
Innym łatwym sposobem na zainicjowanie mapy statycznej jest użycie tej funkcji narzędzia:
Uwaga:
Java 9
możesz użyć Map.ofźródło
Nie lubię składni inicjalizatora statycznego i nie jestem przekonany do anonimowych podklas. Zasadniczo zgadzam się ze wszystkimi wadami używania statycznych inicjatorów i wszystkimi wadami korzystania z anonimowych podklas wymienionych w poprzednich odpowiedziach. Z drugiej strony - profesjonaliści zaprezentowani w tych postach nie są dla mnie wystarczające. Wolę używać metody inicjalizacji statycznej:
źródło
Nie widziałem podejścia, którego używam (i polubiłem), zamieszczonego w żadnej odpowiedzi, więc oto:
Nie lubię używać statycznych inicjatorów, ponieważ są niezgrabne i nie lubię anonimowych klas, ponieważ tworzy nowe klasy dla każdej instancji.
zamiast tego wolę inicjalizację, która wygląda następująco:
niestety metody te nie są częścią standardowej biblioteki Java, dlatego trzeba utworzyć (lub użyć) bibliotekę narzędzi, która definiuje następujące metody:
(możesz użyć opcji „importuj statyczny”, aby uniknąć konieczności prefiksu nazwy metody)
Uznałem, że użyteczne jest zapewnienie podobnych metod statycznych dla innych kolekcji (list, set, sortedSet, sortedMap itp.)
Nie jest tak przyjemny jak inicjalizacja obiektu json, ale jest to krok w tym kierunku, jeśli chodzi o czytelność.
źródło
Ponieważ Java nie obsługuje literałów map, instancje map muszą zawsze być jawnie tworzone i wypełniane.
Na szczęście możliwe jest przybliżenie zachowania literałów map w Javie przy użyciu metod fabrycznych .
Na przykład:
Wynik:
Jest to o wiele wygodniejsze niż tworzenie i wypełnianie mapy na raz elementem.
źródło
JEP 269 zapewnia pewne fabryczne metody dla interfejsu API kolekcji. Te metody fabryczne nie są w aktualnej wersji Java, czyli 8, ale są planowane w wersji Java 9.
Ponieważ
Map
istnieją dwie metody fabryczne:of
iofEntries
. Za pomocąof
można przekazywać naprzemiennie pary klucz / wartość. Na przykład, aby utworzyćMap
podobny{age: 27, major: cs}
:Obecnie istnieje dziesięć przeciążonych wersji
of
, więc możesz utworzyć mapę zawierającą dziesięć par klucz / wartość. Jeśli nie podoba ci się to ograniczenie lub naprzemiennie klucz / wartości, możesz użyćofEntries
:Oba
of
iofEntries
zwrócą niezmienneMap
, więc nie możesz zmienić ich elementów po zakończeniu budowy. Możesz wypróbować te funkcje, korzystając z JDK 9 Early Access .źródło
Cóż ... lubię śliwki;)
źródło
Przeczytałem odpowiedzi i postanowiłem napisać własny kreator map. Kopiuj-wklej i ciesz się.
EDYCJA: Ostatnio
of
dość często znajduję publiczną metodę statyczną i trochę mi się podoba. Dodałem go do kodu i uczyniłem konstruktorem prywatnym, przełączając się w ten sposób na statyczny wzorzec metody fabryki.EDYCJA 2: Jeszcze niedawno nie lubię wywoływanej metody statycznej
of
, ponieważ wygląda ona dość źle podczas korzystania z importu statycznego.mapOf
Zamiast tego zmieniłem nazwę na , dzięki czemu jest bardziej odpowiedni do importowania statycznego.źródło