Jaka jest różnica między obiektami HashMap i Map w Javie?
Jaka jest różnica między poniższymi mapami, które tworzę (w innym pytaniu ludzie odpowiadali używając ich pozornie zamiennie i zastanawiam się, czy / czym się różnią):
HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();
13 answers
Nie ma różnicy między obiektami; masz HashMap<String, Object>
w obu przypadkach. Istnieje różnica w interfejsie musisz do obiektu. W pierwszym przypadku interfejs to HashMap<String, Object>
, natomiast w drugim to Map<String, Object>
. Ale podstawowy obiekt jest taki sam.
Zaletą korzystania z Map<String, Object>
jest to, że możesz zmienić obiekt bazowy na inny rodzaj mapy bez łamania umowy za pomocą dowolnego kodu, który go używa. Jeśli zadeklarujesz to jako HashMap<String, Object>
, musisz Zmień umowę, jeśli chcesz zmienić podstawową realizację.
Przykład: powiedzmy, że piszę tę klasę:
class Foo {
private HashMap<String, Object> things;
private HashMap<String, Object> moreThings;
protected HashMap<String, Object> getThings() {
return this.things;
}
protected HashMap<String, Object> getMoreThings() {
return this.moreThings;
}
public Foo() {
this.things = new HashMap<String, Object>();
this.moreThings = new HashMap<String, Object>();
}
// ...more...
}
Klasa ma kilka wewnętrznych map string->object, które dzieli (poprzez metody accessora) z podklasami. Powiedzmy, że piszę ją HashMap
s na początek, ponieważ myślę, że jest to odpowiednia struktura do użycia podczas pisania klasy.
Później Mary pisze podklasę kodu. Ma coś do zrobienia z obydwoma things
i moreThings
, więc naturalnie umieszcza to w wspólnej metodzie, i używa tego samego typu, którego użyłem nagetThings
/getMoreThings
podczas definiowania jej metody:
class SpecialFoo extends Foo {
private void doSomething(HashMap<String, Object> t) {
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
// ...more...
}
Później uznam, że właściwie lepiej będzie, jeśli użyję TreeMap
zamiast HashMap
W Foo
. Aktualizuję Foo
, zmieniając HashMap
na TreeMap
. Teraz SpecialFoo
już się nie kompiluje, bo zerwałem kontrakt: Foo
zwykłem mówić, że pod warunkiem HashMap
s, ale teraz pod warunkiem TreeMaps
. Więc musimy naprawić SpecialFoo
teraz (i tego typu rzeczy mogą marszczyć poprzez bazę kodową).
O ile nie miałem naprawdę dobrego powodu, aby podzielić się tym, że moja implementacja używała HashMap
(i tak się dzieje), to co powinienem był zrobić, to zadeklarować getThings
i getMoreThings
jako po prostu zwracanie Map<String, Object>
bez bycia bardziej szczegółowym niż to. W rzeczywistości, pomijając dobry powód, aby zrobić coś innego, nawet w Foo
powinienem prawdopodobnie zadeklarować things
i moreThings
jako Map
, a nie HashMap
/TreeMap
:
class Foo {
private Map<String, Object> things; // <== Changed
private Map<String, Object> moreThings; // <== Changed
protected Map<String, Object> getThings() { // <== Changed
return this.things;
}
protected Map<String, Object> getMoreThings() { // <== Changed
return this.moreThings;
}
public Foo() {
this.things = new HashMap<String, Object>();
this.moreThings = new HashMap<String, Object>();
}
// ...more...
}
Zwróć uwagę, jak używam teraz Map<String, Object>
wszędzie, gdzie mogę, tylko będąc dokładnym kiedy tworzę rzeczywiste obiekty.
class SpecialFoo extends Foo {
private void doSomething(Map<String, Object> t) { // <== Changed
// ...
}
public void whatever() {
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
}
...a zmiana {[16] }nie sprawiłaby, że SpecialFoo
przestaną kompilować.
Interfejsy (i klasy bazowe) ujawnijmy tylko tyle, ile jest to konieczne, zachowując naszą elastyczność pod osłonami, aby wprowadzić odpowiednie zmiany. Ogólnie rzecz biorąc, chcemy, aby nasze referencje były jak najbardziej podstawowe. Jeśli nie musimy wiedzieć, że to HashMap
, po prostu nazwij to Map
.
To nie jest ślepa reguła, ale ogólnie, kodowanie do najbardziej ogólnego interfejsu będzie mniej kruche niż kodowanie do czegoś bardziej konkretnego. Gdybym o tym pamiętał, nie stworzyłbym Foo
, który wystawił Mary na porażkę z SpecialFoo
. Gdyby Mary pamiętała o tym, to nawet jeśli nawaliłem Foo
, zadeklarowałaby swoją prywatną metodę Map
zamiast HashMap
i moja zmiana Foo
nie wpłynęłaby na nią kod.
Czasami nie możesz tego zrobić, czasami musisz być konkretny. Ale jeśli nie masz powodu, aby być, błądzić w kierunku najmniej konkretnego interfejsu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-20 08:25:12
Map jest interfejsem, który implementuje HashMap . Różnica polega na tym, że w drugiej implementacji Twoje odniesienie do HashMap pozwoli tylko na korzystanie z funkcji zdefiniowanych w interfejsie mapy, podczas gdy pierwsza pozwoli na korzystanie z dowolnych publicznych funkcji w Hashmapie (która zawiera interfejs mapy).
Prawdopodobnie będzie to miało większy sens, jeśli przeczytasz Sun ' s interface tutorial
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-25 10:56:08
Miałem to zrobić jako komentarz do zaakceptowanej odpowiedzi, ale zrobiło się zbyt odjechane (nienawidzę nie mieć przerw w linii)
Ach, więc różnica jest taka, że w ogólne, Mapa ma pewne metody związane z tym. ale są różne sposoby lub tworzenie mapy, takie jako HashMap, a te różne sposoby zapewniają unikalne metody, które nie wszystkie mapy mają.
Dokładnie--i zawsze chcesz używać najbardziej ogólnego interfejsu, jaki tylko możesz. Rozważ ArrayList vs LinkedList. Ogromna różnica w sposobie ich używania, ale jeśli używasz "listy", możesz łatwo przełączać się między nimi.
W rzeczywistości można zastąpić prawą stronę inicjalizatora bardziej dynamiczną instrukcją. a może coś takiego:
List collection;
if(keepSorted)
collection=new LinkedList();
else
collection=new ArrayList();
W ten sposób, jeśli chcesz wypełnić kolekcję sortowaniem wstawiania, użyjesz listy połączonej (sortowanie wstawiania do listy tablicy jest przestępne.) Ale jeśli nie musisz tego porządkować i tylko dodawać, używasz ArrayList (bardziej efektywny dla innych operacji).
Jest to dość duży stretch tutaj, ponieważ Kolekcje nie są najlepszym przykładem, ale w OO design jednym z najważniejszych pojęć jest korzystanie z fasady interfejsu, aby uzyskać dostęp do różnych obiektów o dokładnie tym samym kodzie.
Edytuj odpowiadając na komentarz:
Jeśli chodzi o twój komentarz do mapy poniżej, tak używanie interfejsu "Mapa" ogranicza cię tylko do tych metod, chyba że oddasz kolekcję z mapy do HashMap (co całkowicie niszczy cel).
Często to, co zrobisz, to utworzenie obiektu i wypełnienie go za pomocą jego określonego typu (HashMap), w jakiejś metodzie "Utwórz" lub "initialize" , ale ta metoda zwróci "mapę", która nie musi być już manipulowana jako HashMap.
Jeśli kiedykolwiek będziesz musiał rzucić tak na marginesie, prawdopodobnie używasz niewłaściwego interfejsu lub Twój kod nie jest wystarczająco dobrze zorganizowany. Należy pamiętać, że dopuszczalne jest, aby jedna sekcja kodu traktowała ją jako "HashMap", podczas gdy drugi traktuje to jako "mapę" , ale to powinno płynąć "w dół". żebyś nigdy nie rzucał.
Zwróć również uwagę na semi-schludny aspekt ról wskazanych przez interfejsy. LinkedList tworzy dobry stos lub kolejkę, ArrayList tworzy dobry stos, ale przerażająca Kolejka (ponownie, usunięcie spowoduje przesunięcie całej listy), więc LinkedList implementuje interfejs kolejki, ArrayList nie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-28 19:05:34
Mapa posiadająca następujące implementacje,
HashMap
Map m = new HashMap();
LinkedHashMap
Map m = new LinkedHashMap();
Mapa Drzewica
Map m = new TreeMap();
WeakHashMap
Map m = new WeakHashMap();
Załóżmy, że stworzyłeś jedną metodę(to tylko kod spudo).
public void HashMap getMap(){
return map;
}
Załóżmy, że wymagania projektu zmieniają się za każdym razem w następujący sposób,
- metoda powinna zwracać Zawartość mapy - trzeba zwrócić
HashMap
. - metoda powinna zwracać klucz map w kolejności wstawiania-należy zmienić typ powrotu
HashMap
naLinkedHashMap
. - metoda powinna zwracać klucz map w posortowanej kolejności - należy zmienić typ powrotu
LinkedHashMap
naTreeMap
.
Jeśli twoja metoda zwraca określone klasy zamiast interfejsu Map
, musisz za każdym razem zmieniać typ zwracanej metody getMap()
.
Ale jeśli użyjesz polimorfizmu funkcji Javy, zamiast zwracać określoną klasę używaną interfejs Map
, prowadzi do ponownego użycia kodu i mniej wpływu, jeśli jakiekolwiek wymagania ulegną zmianie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-16 10:19:26
Jak zauważyli TJ Crowder i Adamski, jedno odnosi się do interfejsu, drugie do konkretnej implementacji interfejsu. Według Joshua Block, zawsze powinieneś próbować kodować interfejsy, aby umożliwić Ci lepszą obsługę zmian w podstawowej implementacji - tzn. jeśli HashMap nagle nie był idealny dla Twojego rozwiązania i potrzebujesz zmienić implementację mapy, możesz nadal używać interfejsu Mapy i zmienić typ instancji.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-28 16:53:00
W drugim przykładzie Referencja "map" jest typu Map
, który jest interfejsem zaimplementowanym przez HashMap
(i inne typy Map
). Interfejs ten jest kontraktem mówiącym, że obiekt mapuje Klucze do wartości i obsługuje różne operacje (np. put
, get
). Mówi nic o implementacji Map
(w tym przypadku a HashMap
).
Drugie podejście jest ogólnie preferowane, ponieważ zazwyczaj nie chcesz narażać konkretnej implementacji mapy na metody wykorzystujące Map
lub poprzez definicję API.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-28 16:51:00
Mapa jest typem statycznym mapy, podczas gdy HashMap jest typem dynamicznym mapy. Oznacza to, że kompilator będzie traktował obiekt map jako obiekt typu Map, nawet jeśli w czasie wykonywania może wskazywać na dowolny jego Podtyp.
Ta praktyka programowania na interfejsach zamiast implementacji ma dodatkową zaletę w postaci pozostawania elastycznym: możesz na przykład zastąpić dynamiczny typ mapy w czasie wykonywania, o ile jest to podtyp mapy (np. LinkedHashMap), oraz zmiana zachowania mapy w locie.
Dobrą zasadą jest pozostawanie jak najbardziej abstrakcyjnym na poziomie API: jeśli na przykład metoda, którą programujesz musi działać na mapach, to wystarczy zadeklarować parametr jako mapę zamiast bardziej rygorystycznego (bo mniej abstrakcyjnego) typu HashMap. W ten sposób konsument twojego API może być elastyczny w kwestii tego, jakiego rodzaju implementację Map chce przekazać Twojej metodzie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-28 17:03:05
Tworzysz te same mapy.
Ale możesz wypełnić różnicę, kiedy go użyjesz. W pierwszym przypadku będziesz mógł użyć specjalnych metod HashMap (ale nie pamiętam nikogo naprawdę użytecznego), i będziesz mógł przekazać go jako parametr HashMap:
public void foo (HashMap<String, Object) { ... }
...
HashMap<String, Object> m1 = ...;
Map<String, Object> m2 = ...;
foo (m1);
foo ((HashMap<String, Object>)m2);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-28 16:56:34
Dodając do góry głosowaną odpowiedź i wiele wyżej podkreślających "bardziej ogólne, lepsze", chciałbym jeszcze trochę pokopać.
Map
jest umową struktury, podczas gdy HashMap
jest implementacją dostarczającą własnych metod radzenia sobie z różnymi rzeczywistymi problemami: jak obliczyć indeks, jaka jest pojemność i jak go zwiększyć, Jak wstawić, jak utrzymać indeks unikalny, itp.
W Map
mamy metodę containsKey(Object key)
:
boolean containsKey(Object key);
JavaDoc:
[[10]}wymaga swoich implementacji, aby ją wdrożyć, ale" jak " jest na wolności, tylko po to, aby zapewnić wraca poprawnie.Boolean java.util.Mapa.containsValue (wartość obiektu)
Zwraca true, jeśli ta mapa mapuje jeden lub więcej kluczy do podanej wartości. Bardziej formalnie, zwraca true wtedy i tylko wtedy, gdy ta mapa zawiera co najmniej jedno odwzorowanie do wartości
v
takiej, że(value==null ? v==null : value.equals(v))
. Operacja ta prawdopodobnie będzie wymagała czasu liniowego w rozmiarze mapy dla większości implementacji interfejsu Mapy.Parametry: wartość
Wartość, której obecność w ta mapa jest do betested
Zwraca: true
Jeśli ta mapa mapuje jeden lub więcej klawiszy do określonego
Wartość:
ClassCastException-jeśli wartość jest nieodpowiedniego typu dla tej mapy (opcjonalne)
NullPointerException-jeśli podana wartość jest null i ta mapa nie zezwala na wartości null (opcjonalne)
W HashMap
:
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
Okazuje się, że HashMap
używa hashcode, aby sprawdzić, czy mapa zawiera klucz. Ma więc zalety algorytmu hash.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-28 08:00:21
Map jest interfejsem, a Hashmap klasą, która to implementuje.
Więc w tej implementacji tworzysz te same obiekty
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-28 16:59:20
Map to interfejs, a Hashmap to klasa implementująca interfejs Map
HashMap jest implementacją Map, więc jest zupełnie taka sama, ale ma metodę "clone ()" jak widzę w reference guide))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-23 12:49:02
HashMap<String, Object> map1 = new HashMap<String, Object>();
Map<String, Object> map2 = new HashMap<String, Object>();
Po pierwsze {[2] } jest interfejsem, który ma inną implementację jak - HashMap
, TreeHashMap
, LinkedHashMap
itd. Interfejs działa jak super klasa dla klasy implementującej. Tak więc zgodnie z zasadą OOP każda konkretna Klasa implementująca {[2] } jest również Map
. Oznacza to, że możemy przypisać / umieścić dowolną zmienną typu HashMap
do zmiennej typu Map
bez żadnego typu odlewania.
W tym przypadku możemy przypisać map1
do map2
bez żadnego odlewania lub utraty danych -
map2 = map1
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-09 19:10:08