Collections.emptyMap () a nowy HashMap ()

143

Jakie są sytuacje, w których mogę skorzystać Collections.emptyMap()? Dokumentacja mówi, że mogę użyć tej metody, jeśli chcę, aby moja kolekcja była niezmienna.

Dlaczego miałbym chcieć niezmiennej, pustej kolekcji? O co chodzi?

Vinoth Kumar CM
źródło
12
+1 do pytania, ponieważ nigdy wcześniej nie widziałem tej statycznej metody
Tim Bender,
możesz też rzucić okiem na soczewki scala soczewki google scala . EmptyMap i immutableMap mogą służyć do tworzenia obiektów, które są niezmienne. pusta mapa jest punktem początkowym. Za każdym razem, gdy dodawany jest element, sama mapa jest zastępowana mapą zawierającą stary element i nowe elementy. Chodzi o to, że dostęp do obiektu jest bezpieczny.
michael_s
1
To jak Objective-C [NSArray array], zwraca obiekt, który chociaż nie nadaje się do użytku, ale istnieje. Możesz więc bawić się nim jak normalnym przedmiotem i nie otrzymać błędu.
Shane Hsu,

Odpowiedzi:

144

Od Effective Java , poz # 43 - "Return empty arrays or collections, not null"demonstruje powrocie pustą kolekcję, a może nawet demonstruje przy użyciu tych emptyList(), emptySet()oraz emptyMap()metody na zbiorach klasy, aby uzyskać pustą kolekcję, która ma również dodatkową zaletę, że są niezmienne. Z przedmiotu nr 15 "Minimize Mutability" .

Z kolekcji-emptySet-Collections-emptyList-Collections

To rodzaj idiomu programowania. To jest dla ludzi, którzy nie chcą zmiennych null. Więc zanim zestaw zostanie zainicjowany, mogą użyć pustego zestawu.

Uwaga: poniższy kod to tylko przykład (zmień go w zależności od przypadku użycia):

private Set myset = Collections.emptySet();

void initSet() {
   myset = new HashSet();
}
void deleteSet() {
   myset = Collections.emptySet();
}

Te metody mają kilka zalet:

  1. Są bardziej zwięzłe, ponieważ nie trzeba jawnie wpisywać typu ogólnego kolekcji - zwykle jest on po prostu wywnioskowany z kontekstu wywołania metody.

  2. Są bardziej wydajne, ponieważ nie zawracają sobie głowy tworzeniem nowych obiektów; po prostu ponownie wykorzystują istniejący pusty i niezmienny obiekt. Ten efekt jest ogólnie bardzo niewielki, ale czasami (no cóż, rzadko) ważny.

Sumit Singh
źródło
15
+1 za odniesienie do Effective Java. Jedna wada: zalecałbym parametryzację Seti HashSetw twoich przykładach, ponieważ celem emptySet()metody i przyjaciół (w przeciwieństwie do stałych Collections.EMPTY_SETitp.) Jest to, że dobrze się bawią z rodzajami. Ponadto używanie funkcji (typów surowych), która jest przestarzała od czasu Java 5, nie jest dobrą pomocą dydaktyczną.
Daniel Pryden
4
Pozostaję nieprzekonany ... Czy nie chodzi o to, aby używać Collectionzamiast tego, nullaby uniknąć Exceptionsrzucenia się na kolejne operacje? Użycie niezmiennej kolekcji spowodowałoby po prostu inny rodzaj wyjątku, jaki sobie wyobrażam. A przypisanie z nullpewnością nie jest mniej wydajne niż przypisanie niezmiennej stałej.
fgysin przywraca Monikę
14
@fgysin: Jeśli masz interfejs API, w którym klienci mają modyfikować kolekcję, to tak, nie ma sensu zwracać niezmiennej kolekcji. Ale jeśli masz interfejs API, w którym zwracasz kolekcję, której klienci nie mogą modyfikować i powinni po prostu iterować, to zwrócenie widoku do podstawowej kolekcji ma sens, aby upewnić się, że „zły” klient nie zmodyfikuje przypadkowo posiadanych kolekcji przez Ciebie. Zwracanie pustej kolekcji zamiast wartości null oznacza, że ​​klient nie musi sprawdzać wartości null przed użyciem kolekcji, dzięki czemu kod klienta jest bardziej przejrzysty.
Jean Hominal
4
public boolean setExists() { return !myset.equals(Collections.emptySet()); }
assylias
5
W twoim przykładzie jawnie sprawdzasz pusty zbiór i używasz go jako wartości wartowniczej, a twoja klasa jest zmienna. Twój mały przykład używa wartowników i zmienności, co jest dokładnie tym, co próbuje zapobiec Collection.emptySet ().
Marc O'Morain
33

Z mojego osobistego doświadczenia wynika, że ​​jest to bardzo przydatne w przypadkach, gdy API wymaga zbioru parametrów, ale nie masz nic do dostarczenia. Na przykład możesz mieć interfejs API, który wygląda mniej więcej tak i nie zezwala na zerowe odwołania:

public ResultSet executeQuery(String query, Map<String, Object> queryParameters);

Jeśli masz zapytanie, które nie przyjmuje żadnych parametrów, z pewnością trochę marnotrawstwem jest tworzenie HashMap, które obejmuje alokację tablicy, podczas gdy możesz po prostu przekazać `` Pustą mapę '', która jest faktycznie stałą, sposób jej implementacji w java.util.Collections.

Affe
źródło
22

Dlaczego miałbym chcieć niezmiennej, pustej kolekcji? O co chodzi?

Istnieją tutaj dwie różne koncepcje, które wydają się dziwne, gdy ogląda się je razem. Ma to większy sens, gdy traktujesz te dwie koncepcje oddzielnie.

  • Po pierwsze, jeśli to tylko możliwe, wolisz używać niezmiennej kolekcji, a nie mutowalnej. Korzyści z odporności są dobrze udokumentowane gdzie indziej .

  • Po drugie, wolisz używać pustej kolekcji niż używać null jako wartownika. Jest to dobrze opisane tutaj . Oznacza to, że będziesz mieć znacznie bardziej przejrzysty i łatwiejszy do zrozumienia kod, z mniejszą liczbą miejsc do ukrycia błędów.

Więc jeśli masz kod, który wymaga mapy, lepiej jest przekazać pustą mapę niż wartość null, aby wskazać brak mapy. W większości przypadków, gdy używasz mapy, lepiej jest użyć niezmiennej mapy. Dlatego właśnie istnieje wygodna funkcja tworzenia niezmiennej pustej mapy.

Marc O'Morain
źródło
8

Jest kilka przypadków, w których wolisz używać niezmiennych map, list, zestawów lub innych typów kolekcji.

Pierwszym i prawdopodobnie najważniejszym przypadkiem użycia jest to, że zawsze, gdy zwracasz wynik zapytania lub obliczenia, które zwróciłyby zestaw (lub listę lub mapę) wyników, powinieneś preferować użycie niezmiennych struktur danych.

W tym przypadku wolę zwracać ich niezmienne wersje, ponieważ odzwierciedla to faktyczną niezmienność zestawu wyników obliczeń znacznie wyraźniej - bez względu na to, co zrobisz z danymi później, zestaw wyników otrzymanych z zapytania nie powinien zmiana.

Drugim typowym przypadkiem użycia jest podanie argumentu jako danych wejściowych do metody lub usługi. O ile nie spodziewasz się, że kolekcja wejściowa zostanie zmodyfikowana przez usługę lub metodę (co zwykle jest naprawdę złym pomysłem projektowym), przekazanie niezmiennej kolekcji zamiast mutowalnej może być w wielu przypadkach rozsądnym i bezpiecznym wyborem.

Myślę o tym jako o konwencji „przekazywania wartości” .

Mówiąc bardziej ogólnie - rozsądną praktyką jest stosowanie niezmiennych struktur danych, gdy dane przekraczają granice modułów lub usług. To znacznie ułatwia wnioskowanie o różnicach między (niezmiennym) wejściem / wyjściem a zmiennym stanem wewnętrznym.

Bardzo korzystnym efektem ubocznym jest zwiększone bezpieczeństwo i bezpieczeństwo wątków Twoich modułów / usług oraz zapewnia czystsze oddzielenie problemów.

Innym dobrym powodem do używania Collections.empty*()metod jest ich zauważalny brak słownictwa. W erze przed Java7, jeśli miałeś kolekcję ogólną, musiałeś posypać adnotacjami typu ogólnego w każdym miejscu.

Wystarczy porównać te dwie deklaracje:

Map<Foo, Comparable<? extends Bar>> fooBarMap = new HashMap<Foo, Comparable<? extends Bar>>();

przeciw:

Map<Foo, Comparable<? extends Bar>> fooBarMap = Collections.emptyMap();

Ta ostatnia wyraźnie wygrywa pod względem czytelności na dwa ważne sposoby:

  1. W pierwszej deklaracji cała instancja pustej mapy jest ukryta w szumie deklaracji typu ogólnego, co sprawia, że ​​zasadniczo trywialna deklaracja jest o wiele bardziej tajemnicza, niż powinna.
  2. Oprócz zauważalnego braku adnotacji typu ogólnego po prawej stronie, druga wersja wyraźnie stwierdza, że ​​mapa jest zainicjowana jako pusta mapa. Ponadto - wiedząc, że ta metoda zwraca niezmienną mapę, teraz łatwiej jest mi znaleźć miejsce, w którym fooBarMapjest przypisywana kolejna niepusta wartość, po prostu wyszukując /fooBarMap =/.
Roland Tepp
źródło
5

Po pierwsze, możesz uciec dzięki udostępnianiu referencji. A new HashMap()etc będzie wymagało przydzielonego obiektu i prawdopodobnie kilku dodatkowych elementów do przechowywania danych, ale potrzebujesz tylko jednej kopii niezmiennej pustej kolekcji (lista, zestaw, mapa lub dowolna inna). To sprawia, że ​​jest to oczywisty wybór, gdy wywoływana metoda musi akceptować Mapę, ale nie musi jej edytować.

Proponuję sprawdzić efektywną Javę Josha Blocha , która wymienia kilka bardzo ładnych atrybutów niezmiennych obiektów (w tym bezpieczeństwo wątków).

Jeff Bowman
źródło
3

Może to być przydatne, gdy masz funkcję, która zwraca immutable collectiona, aw niektórych sytuacjach nie ma danych do zwrócenia, więc zamiast zwracać nullmożesz zwrócićemptyMap()

Ułatwia kod i zapobiega NullPointerException

iTech
źródło
3

Przez większość czasu używamy a constructordo tworzenia nowego empty map. Ale Collections methodsoferuje kilka zalet, aby stworzyć empty mapusingstatic method java.util.Collections.emptyMap()

  1. Są bardziej zwięzłe, ponieważ nie trzeba jawnie wpisywać typu ogólnego kolekcji - zwykle jest on po prostu wywnioskowany z kontekstu wywołania metody.

  2. Są bardziej wydajne, ponieważ nie zawracają sobie głowy tworzeniem nowych obiektów; po prostu ponownie wykorzystują istniejący pusty i niezmienny obiekt. Ten efekt jest ogólnie bardzo niewielki, ale czasami (no cóż, rzadko) ważny.

subodh
źródło
2

Dlaczego miałbym chcieć niezmiennej, pustej kolekcji? O co chodzi?

Z tego samego powodu, którego używałbyś Collections.unmodifiableMap()w pewnym momencie. Chcesz zwrócić wystąpienie Map, które zgłasza wyjątek, jeśli użytkownik spróbuje go zmodyfikować. To tylko szczególny przypadek: pusta mapa.

Ryan Stewart
źródło
1

Dlaczego miałbym chcieć niezmiennej, pustej kolekcji? O co chodzi?

Z tych samych powodów, dla których możesz chcieć niezmiennych obiektów. Przede wszystkim dlatego, że możesz spać bezpiecznie w nocy, wiedząc, że wiele wątków może uzyskać dostęp do tego samego wystąpienia obiektu i że wszystkie będą widzieć te same wartości. Brak elementów w kolekcji jest nadal prawidłową wartością, którą chciałbyś zachować.

vegemite4me
źródło