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>();
Author: 030, 2009-08-28

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.

Gdybym to zrobił, Maria zrobiłaby to:]}
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.

 362
Author: T.J. Crowder,
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

 52
Author: Graphics Noob,
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.

 17
Author: Bill K,
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

Tutaj wpisz opis obrazka

Mapa posiadająca następujące implementacje,

  1. HashMap Map m = new HashMap();

  2. LinkedHashMap Map m = new LinkedHashMap();

  3. Mapa Drzewica Map m = new TreeMap();

  4. 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,

  1. metoda powinna zwracać Zawartość mapy - trzeba zwrócić HashMap.
  2. metoda powinna zwracać klucz map w kolejności wstawiania-należy zmienić typ powrotu HashMap na LinkedHashMap.
  3. metoda powinna zwracać klucz map w posortowanej kolejności - należy zmienić typ powrotu LinkedHashMap na TreeMap.

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.

 16
Author: atish shimpi,
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.

 12
Author: aperkins,
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.

 8
Author: Adamski,
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.

 8
Author: Matthias,
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); 
 3
Author: Roman,
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.

Przyjrzyjmy się kodowi źródłowemu:

W Map mamy metodę containsKey(Object key):

boolean containsKey(Object key);

JavaDoc:

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)

[[10]}wymaga swoich implementacji, aby ją wdrożyć, ale" jak " jest na wolności, tylko po to, aby zapewnić wraca poprawnie.

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.

 2
Author: WesternGun,
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

 1
Author: Diego Dias,
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

 1
Author: ,
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-05-09 06:24:26

HashMap jest implementacją Map, więc jest zupełnie taka sama, ale ma metodę "clone ()" jak widzę w reference guide))

 0
Author: kolyaseg,
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
 0
Author: Razib,
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