Was ist der Unterschied zwischen den folgenden Karten, die ich erstelle (in einer anderen Frage haben die Leute scheinbar austauschbar mit ihnen geantwortet und ich frage mich, ob / wie sie sich unterscheiden):
HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();
java
dictionary
hashmap
Tony Stark
quelle
quelle
Antworten:
Es gibt keinen Unterschied zwischen den Objekten; Sie haben
HashMap<String, Object>
in beiden Fällen eine. Es gibt einen Unterschied in der Schnittstelle, die Sie zum Objekt haben. Im ersten Fall ist die SchnittstelleHashMap<String, Object>
, im zweiten FallMap<String, Object>
. Das zugrunde liegende Objekt ist jedoch dasselbe.Der Vorteil der Verwendung
Map<String, Object>
besteht darin, dass Sie das zugrunde liegende Objekt in eine andere Art von Karte ändern können, ohne Ihren Vertrag mit Code zu brechen, der es verwendet. Wenn Sie dies als deklarierenHashMap<String, Object>
, müssen Sie Ihren Vertrag ändern, wenn Sie die zugrunde liegende Implementierung ändern möchten.Beispiel: Nehmen wir an, ich schreibe diese Klasse:
Die Klasse verfügt über einige interne Zuordnungen von Zeichenfolgen-> Objekten, die sie (über Zugriffsmethoden) mit Unterklassen teilt. Nehmen wir an, ich schreibe es zunächst mit
HashMap
s, weil ich denke, dass dies die geeignete Struktur für das Schreiben der Klasse ist.Später schreibt Mary Code in Unterklassen. Sie hat etwas , das sie mit den beiden tun muss
things
undmoreThings
, so natürlich macht sie , dass in einer gemeinsamen Methode, und sie verwendet den gleichen Typ I verwendetengetThings
/getMoreThings
bei der Definition ihrer Methode:Später entscheide ich, dass es eigentlich besser ist, wenn ich
TreeMap
anstelle vonHashMap
in verwendeFoo
. Ich aktualisiereFoo
und wechsleHashMap
zuTreeMap
. Jetzt wirdSpecialFoo
nicht mehr kompiliert, weil ich den Vertrag gebrochen habe:Foo
Früher hieß esHashMap
s, aber jetztTreeMaps
stattdessen. Also müssen wir jetzt Abhilfe schaffenSpecialFoo
(und so etwas kann sich durch eine Codebasis kräuseln).Wenn ich nicht einen wirklich guten Grund hatte zu teilen, dass meine Implementierung ein verwendet
HashMap
(und das passiert), hätte ich deklarierengetThings
undgetMoreThings
einfach zurückkehren sollen,Map<String, Object>
ohne spezifischer zu sein. In der Tat, abgesehen von einem guten Grund, etwas anderes zu tun,Foo
sollte ich selbst innerhalb wahrscheinlich erklärenthings
undmoreThings
alsMap
, nichtHashMap
/TreeMap
:Beachten Sie, wie ich jetzt
Map<String, Object>
überall verwende, wo ich kann, und nur dann spezifisch bin, wenn ich die tatsächlichen Objekte erstelle.Wenn ich das getan hätte, hätte Mary dies getan:
... und das Ändern
Foo
hätte nicht dazu geführt,SpecialFoo
dass das Kompilieren aufgehört hätte.Über Schnittstellen (und Basisklassen) können wir nur so viel wie nötig preisgeben und unsere Flexibilität unter Verschluss halten, um gegebenenfalls Änderungen vorzunehmen. Im Allgemeinen möchten wir, dass unsere Referenzen so einfach wie möglich sind. Wenn wir nicht wissen müssen, dass es ein ist
HashMap
, nennen Sie es einfach einMap
.Dies ist keine blinde Regel, aber im Allgemeinen ist die Codierung für die allgemeinste Schnittstelle weniger spröde als die Codierung für etwas Spezifischeres. Wenn ich mich daran erinnert hätte, hätte ich keine geschaffen
Foo
, die Mary zum Scheitern verurteilt hätteSpecialFoo
. Wenn Mary sich daran erinnert hätte, hätte sie, obwohl ich es vermasseltFoo
habe, ihre private Methode mitMap
statt deklariert,HashMap
und der Vertrag meiner ÄnderungFoo
hätte sich nicht auf ihren Code ausgewirkt.Manchmal kann man das nicht, manchmal muss man spezifisch sein. Aber wenn Sie keinen Grund dazu haben, gehen Sie zur am wenigsten spezifischen Schnittstelle.
quelle
Map ist eine Schnittstelle, die HashMap implementiert. Der Unterschied besteht darin, dass in der zweiten Implementierung Ihr Verweis auf die HashMap nur die Verwendung von Funktionen erlaubt, die in der Map-Schnittstelle definiert sind, während die erste die Verwendung aller öffentlichen Funktionen in HashMap (einschließlich der Map-Schnittstelle) erlaubt.
Es ist wahrscheinlich sinnvoller, wenn Sie das Tutorial zur Benutzeroberfläche von Sun lesen
quelle
Map hat die folgenden Implementierungen:
HashMap
Map m = new HashMap();
LinkedHashMap
Map m = new LinkedHashMap();
Baumkarte
Map m = new TreeMap();
WeakHashMap
Map m = new WeakHashMap();
Angenommen, Sie haben eine Methode erstellt (dies ist nur ein Pseudocode).
Angenommen, Ihre Projektanforderungen ändern sich:
HashMap
.HashMap
zuLinkedHashMap
.LinkedHashMap
zuTreeMap
.Wenn Ihre Methode bestimmte Klassen anstelle von etwas zurückgibt, das die
Map
Schnittstelle implementiert , müssen Sie den Rückgabetyp dergetMap()
Methode jedes Mal ändern .Wenn Sie jedoch die Polymorphismusfunktion von Java verwenden und nicht bestimmte Klassen zurückgeben, sondern die Schnittstelle verwenden
Map
, wird die Wiederverwendbarkeit des Codes verbessert und die Auswirkung von Anforderungsänderungen verringert.quelle
Ich wollte dies nur als Kommentar zu der akzeptierten Antwort tun, aber es wurde zu funky (ich hasse es, keine Zeilenumbrüche zu haben).
Genau - und Sie möchten immer die allgemeinste Schnittstelle verwenden, die Sie können. Betrachten Sie ArrayList vs LinkedList. Ein großer Unterschied in der Art und Weise, wie Sie sie verwenden, aber wenn Sie "Liste" verwenden, können Sie leicht zwischen ihnen wechseln.
Tatsächlich können Sie die rechte Seite des Initialisierers durch eine dynamischere Anweisung ersetzen. wie wäre es mit so etwas:
Auf diese Weise würden Sie, wenn Sie die Sammlung mit einer Einfügungssortierung füllen möchten, eine verknüpfte Liste verwenden (eine Einfügungssortierung in eine Array-Liste ist kriminell). Wenn Sie sie jedoch nicht sortieren müssen und nur anhängen, Sie verwenden eine ArrayList (effizienter für andere Operationen).
Dies ist hier eine ziemlich große Strecke, da Sammlungen nicht das beste Beispiel sind, aber im OO-Design ist eines der wichtigsten Konzepte die Verwendung der Schnittstellenfassade, um auf verschiedene Objekte mit genau demselben Code zuzugreifen.
Bearbeiten Antwort auf Kommentar:
Was Ihren Kartenkommentar unten betrifft, so beschränkt sich Ja, wenn Sie die "Map" -Schnittstelle verwenden, nur auf diese Methoden, es sei denn, Sie setzen die Sammlung von Map in HashMap zurück (was den Zweck VOLLSTÄNDIG zunichte macht).
Oft erstellen Sie ein Objekt und füllen es mit seinem spezifischen Typ (HashMap) in einer Art "create" - oder "initialize" -Methode aus. Diese Methode gibt jedoch eine "Map" zurück, die nicht benötigt wird als HashMap nicht mehr manipuliert.
Wenn Sie übrigens jemals Casting durchführen müssen, verwenden Sie wahrscheinlich die falsche Oberfläche oder Ihr Code ist nicht gut genug strukturiert. Beachten Sie, dass es akzeptabel ist, dass ein Abschnitt Ihres Codes ihn als "HashMap" behandelt, während der andere ihn als "Map" behandelt. Dies sollte jedoch "down" erfolgen. so dass Sie nie gießen.
Beachten Sie auch den halbwegs ordentlichen Aspekt der Rollen, die durch Schnittstellen angezeigt werden. Eine LinkedList macht einen guten Stapel oder eine gute Warteschlange, eine ArrayList macht einen guten Stapel, aber eine schreckliche Warteschlange (wieder würde ein Entfernen eine Verschiebung der gesamten Liste verursachen), so dass LinkedList die Warteschlangenschnittstelle implementiert, ArrayList nicht.
quelle
Wie von TJ Crowder und Adamski festgestellt, bezieht sich eine Referenz auf eine Schnittstelle, die andere auf eine bestimmte Implementierung der Schnittstelle. Laut Joshua Block sollten Sie immer versuchen, Code für Schnittstellen zu erstellen, damit Sie Änderungen an der zugrunde liegenden Implementierung besser verarbeiten können. Wenn HashMap plötzlich nicht mehr für Ihre Lösung geeignet ist und Sie die Map-Implementierung ändern müssen, können Sie die Map dennoch verwenden Schnittstelle und ändern Sie den Instanziierungstyp.
quelle
In Ihrem zweiten Beispiel ist die "Map" -Referenz vom Typ
Map
, einer Schnittstelle, die vonHashMap
(und anderen Arten vonMap
) implementiert wird . Diese Schnittstelle ist ein Vertrag zu sagen , dass das Objekt abbildet Schlüssel auf Werte und unterstützt verschiedene Operationen (zBput
,get
). Es sagt nichts über die Implementierung derMap
(in diesem Fall aHashMap
).Der zweite Ansatz wird im Allgemeinen bevorzugt, da Sie die spezifische Kartenimplementierung normalerweise nicht Methoden aussetzen möchten, die die
Map
API-Definition oder verwenden.quelle
Map ist der statische Kartentyp, während HashMap der dynamische Kartentyp ist. Dies bedeutet, dass der Compiler Ihr Map-Objekt als Map-Objekt behandelt, obwohl es zur Laufzeit auf einen beliebigen Subtyp verweisen kann.
Diese Praxis des Programmierens gegen Schnittstellen anstelle von Implementierungen hat den zusätzlichen Vorteil, dass Sie flexibel bleiben: Sie können beispielsweise den dynamischen Kartentyp zur Laufzeit ersetzen, sofern es sich um einen Untertyp der Karte handelt (z. B. LinkedHashMap), und das Verhalten der Karte ändern die Fliege.
Eine gute Faustregel ist, auf API-Ebene so abstrakt wie möglich zu bleiben: Wenn beispielsweise eine von Ihnen programmierte Methode für Maps arbeiten muss, reicht es aus, einen Parameter anstelle des strengeren (weil weniger abstrakten) HashMap-Typs als Map zu deklarieren . Auf diese Weise kann der Benutzer Ihrer API flexibel festlegen, welche Art von Map-Implementierung er an Ihre Methode übergeben möchte.
quelle
Ich möchte noch ein bisschen mehr graben, indem ich zu der am besten bewerteten Antwort hinzufüge und viele darüber, die "generischer, besser" betonen.
Map
ist der Strukturvertrag, währendHashMap
eine Implementierung ihre eigenen Methoden zur Bewältigung verschiedener realer Probleme bereitstellt: wie man den Index berechnet, wie groß die Kapazität ist und wie man ihn erhöht, wie man ihn einfügt, wie man den Index eindeutig hält usw.Schauen wir uns den Quellcode an:
In haben
Map
wir die Methode voncontainsKey(Object key)
:JavaDoc:
Es erfordert seine Implementierungen, um es zu implementieren, aber das "How to" ist in seiner Freiheit, nur um sicherzustellen, dass es korrekt zurückgibt.
In
HashMap
:Es stellt sich heraus, dass
HashMap
Hashcode verwendet wird, um zu testen, ob diese Karte den Schlüssel enthält. Es hat also den Vorteil eines Hash-Algorithmus.quelle
Sie erstellen die gleichen Karten.
Sie können den Unterschied jedoch ausfüllen, wenn Sie ihn verwenden. Im ersten Fall können Sie spezielle HashMap-Methoden verwenden (aber ich erinnere mich an niemanden, der wirklich nützlich ist), und Sie können ihn als HashMap-Parameter übergeben:
quelle
Map ist eine Schnittstelle und Hashmap ist eine Klasse, die Map Interface implementiert
quelle
Map ist die Schnittstelle und Hashmap ist die Klasse, die das implementiert.
In dieser Implementierung erstellen Sie also dieselben Objekte
quelle
HashMap ist eine Implementierung von Map, es ist also ziemlich dasselbe, hat aber die "clone ()" - Methode, wie ich im Referenzhandbuch sehe))
quelle
Zunächst einmal
Map
ist eine Schnittstelle , es andere Implementierung hat wie -HashMap
,TreeHashMap
,LinkedHashMap
arbeitet usw. Schnittstelle wie ein Superklasse für die implementierende Klasse. Nach der OOP-Regel ist also jede konkrete Klasse, die implementiertMap
wird, auch eineMap
. Das Mittel können wir jeden zuweisen / Put -HashMap
Typ Variable auf eineMap
Variable vom Typ ohne jede Art von Gießen.In diesem Fall können wir zuordnen
map1
zumap2
ohne Gießen oder jede verlierende von Daten -quelle