Ich war überrascht, dass Map<?,?>
das kein ist Collection<?>
.
Ich dachte, es wäre sehr sinnvoll, wenn es als solches deklariert würde:
public interface Map<K,V> extends Collection<Map.Entry<K,V>>
Immerhin ist a Map<K,V>
eine Sammlung von Map.Entry<K,V>
, nicht wahr?
Gibt es einen guten Grund, warum es nicht als solches implementiert wird?
Vielen Dank an Cletus für die maßgebliche Antwort, aber ich frage mich immer noch, warum, wenn Sie bereits ein Map<K,V>
as Set<Map.Entries<K,V>>
(via entrySet()
) anzeigen können , diese Schnittstelle nicht einfach erweitert wird.
Wenn a a
Map
istCollection
, was sind die Elemente? Die einzig vernünftige Antwort lautet "Schlüssel-Wert-Paare".
Genau, interface Map<K,V> extends Set<Map.Entry<K,V>>
wäre toll!
Dies bietet jedoch eine sehr begrenzte (und nicht besonders nützliche)
Map
Abstraktion.
Aber wenn dies der Fall ist, warum wird es dann entrySet
von der Schnittstelle angegeben? Es muss irgendwie nützlich sein (und ich denke, es ist einfach, für diese Position zu argumentieren!).
Sie können weder fragen, welchem Wert ein bestimmter Schlüssel zugeordnet ist, noch den Eintrag für einen bestimmten Schlüssel löschen, ohne zu wissen, welchem Wert er zugeordnet ist.
Ich sage nicht, dass das alles ist, was dazu gehört Map
! Es kann und sollte alle anderen Methoden beibehalten (außer entrySet
, die jetzt redundant sind)!
quelle
Antworten:
Aus den häufig gestellten Fragen zum Java Collections API Design :
Update: Ich denke, das Zitat beantwortet die meisten Fragen. Es lohnt sich, den Teil zu betonen, dass eine Sammlung von Einträgen keine besonders nützliche Abstraktion ist. Beispielsweise:
würde erlauben:
(unter der Annahme einer
entry()
Methode, die a erstelltMap.Entry
Instanz erstellt)Map
s erfordern eindeutige Schlüssel, damit dies verletzt wird. Oder wenn Sie einemSet
Eintrag eindeutige Schlüssel auferlegen , ist diesSet
im Allgemeinen kein wirklicher . Es ist einSet
mit weiteren Einschränkungen.Man könnte wohl die
equals()
/hashCode()
Beziehung für sagenMap.Entry
nur der Schlüssel war, aber selbst das hat Probleme. Was noch wichtiger ist: Fügt es wirklich einen Mehrwert hinzu? Möglicherweise bricht diese Abstraktion zusammen, wenn Sie sich die Eckfälle ansehen.Es ist erwähnenswert, dass das
HashSet
tatsächlich als implementiert istHashMap
, nicht umgekehrt. Dies ist lediglich ein Implementierungsdetail, aber dennoch interessant.Der Hauptgrund für
entrySet()
die Existenz besteht darin, das Durchlaufen zu vereinfachen, sodass Sie die Schlüssel nicht durchlaufen und dann den Schlüssel nachschlagen müssen. Nehmen Sie es nicht als Anscheinsbeweis dafür, dass aMap
einSet
Eintrag sein sollte (imho).quelle
entrySet()
nichtremove
, während sie nicht unterstütztadd
(wahrscheinlich wirftUnsupportedException
). Also ich sehe Ihren Punkt, aberadd
können genauso behandelt werden wie dieentrySet()
Ansicht: Einige Vorgänge zulassen und andere nicht zulassen. Eine natürliche Antwort lautet natürlich: "Was unterstütztSet
das nichtadd
?" -entrySet()
Nun , wenn ein solches Verhalten akzeptabel ist , warum ist es dann nicht akzeptabelthis
? Trotzdem bin ich bereits größtenteils davon überzeugt, warum dies keine so großartige Idee ist, wie ich einst dachte, aber ich denke immer noch, dass es einer weiteren Debatte wert ist, schon allein, um mein eigenes Verständnis dafür zu bereichern, was ein gutes API / OOP-Design ausmacht.Collection
Verlängerung denktMap
?Obwohl Sie eine Reihe von Antworten erhalten haben, die Ihre Frage ziemlich direkt abdecken, halte ich es für nützlich, einen Schritt zurückzutreten und die Frage etwas allgemeiner zu betrachten. Das heißt, nicht speziell darauf zu achten, wie die Java-Bibliothek geschrieben wird, und darauf, warum sie so geschrieben ist.
Das Problem hierbei ist, dass die Vererbung nur eine Art von Gemeinsamkeit modelliert . Wenn Sie zwei Dinge auswählen, die beide "sammlungsartig" erscheinen, können Sie wahrscheinlich 8 oder 10 Dinge auswählen, die sie gemeinsam haben. Wenn Sie ein anderes Paar "sammlungsähnlicher" Dinge auswählen, haben sie auch 8 oder 10 Dinge gemeinsam - aber sie sind nicht die gleichen 8 oder 10 Dinge wie das erste Paar.
Wenn Sie an einem Dutzend aussehen oder so anders „-Kollektion-like“ Dinge, praktisch jeder von ihnen wird wahrscheinlich so etwas wie 8 oder 10 Merkmale gemeinsam mit mindestens einem anderen - aber wenn man sieht , was über geteilt ist jeder ein von ihnen bleibt praktisch nichts übrig.
Dies ist eine Situation, in der die Vererbung (insbesondere die Einzelvererbung) einfach nicht gut modelliert wird. Es gibt keine klare Trennlinie zwischen diesen Sammlungen und welchen nicht - aber wenn Sie eine aussagekräftige Sammlungsklasse definieren möchten, müssen Sie einige davon weglassen. Wenn Sie nur einige davon weglassen, kann Ihre Collection-Klasse nur eine recht spärliche Oberfläche bereitstellen. Wenn Sie mehr weglassen, können Sie ihm eine reichhaltigere Oberfläche geben.
Einige haben auch die Möglichkeit, im Grunde zu sagen: "Diese Art von Sammlung unterstützt Operation X, aber Sie dürfen sie nicht verwenden, indem Sie von einer Basisklasse ableiten, die X definiert, aber der Versuch, die abgeleitete Klasse 'X' zu verwenden, schlägt fehl (z durch Auslösen einer Ausnahme).
Das lässt immer noch ein Problem offen: Fast unabhängig davon, was Sie weglassen und welches Sie einsetzen, müssen Sie eine harte Linie zwischen den Klassen ziehen, die rein und die raus sind. Egal, wo Sie diese Linie ziehen, Sie werden eine klare, eher künstliche Trennung zwischen einigen Dingen haben, die ziemlich ähnlich sind.
quelle
Ich denke das Warum ist subjektiv.
In C # wird meiner Meinung
Dictionary
nach eine Sammlung erweitert oder zumindest implementiert:Auch in Pharo Smalltak:
Bei einigen Methoden besteht jedoch eine Asymmetrie. Zum Beispiel nimmt
collect:
will eine Assoziation (das Äquivalent eines Eintrags), währenddo:
die Werte genommen werden. Sie bieten eine weitere MethodekeysAndValuesDo:
, um das Wörterbuch nach Eintrag zu iterieren.Add:
nimmt eine Assoziation, wurde aberremove:
"unterdrückt":Es ist also definitiv machbar, führt aber zu einigen anderen Problemen in Bezug auf die Klassenhierarchie.
Was besser ist, ist subjektiv.
quelle
Die Antwort von Cletus ist gut, aber ich möchte einen semantischen Ansatz hinzufügen. Um beides zu kombinieren, macht es keinen Sinn, sich vorzustellen, dass Sie über die Erfassungsschnittstelle ein Schlüssel-Wert-Paar hinzufügen und der Schlüssel bereits vorhanden ist. Die Map-Schnittstelle erlaubt nur einen Wert, der dem Schlüssel zugeordnet ist. Wenn Sie jedoch den vorhandenen Eintrag automatisch mit demselben Schlüssel entfernen, hat die Sammlung nach dem Hinzufügen dieselbe Größe wie zuvor - für eine Sammlung sehr unerwartet.
quelle
Set
funktioniert, undSet
nicht zu implementierenCollection
.Java-Sammlungen sind kaputt. Es fehlt eine Schnittstelle, die von Relation. Daher erweitert Map Relation Extend Set. Beziehungen (auch Multi-Maps genannt) haben eindeutige Name-Wert-Paare. Karten (auch "Funktionen" genannt) haben eindeutige Namen (oder Schlüssel), die natürlich Werten zugeordnet sind. Sequenzen erweitern Maps (wobei jeder Schlüssel eine Ganzzahl> 0 ist). Taschen (oder mehrere Sätze) erweitern Karten (wobei jeder Schlüssel ein Element ist und jeder Wert die Häufigkeit angibt, mit der das Element in der Tasche erscheint).
Diese Struktur würde die Überschneidung, Vereinigung usw. einer Reihe von "Sammlungen" ermöglichen. Daher sollte die Hierarchie sein:
Sun / Oracle / Java ppl - bitte machen Sie es beim nächsten Mal richtig. Vielen Dank.
quelle
Wenn Sie sich die jeweilige Datenstruktur ansehen, können Sie leicht erraten, warum dies
Map
nicht Teil von istCollection
. JederCollection
speichert einen einzelnen Wert, wobei alsMap
Schlüssel-Wert-Paar gespeichert wird. Daher sind Methoden in derCollection
Schnittstelle für dieMap
Schnittstelle nicht kompatibel . Zum Beispiel in habenCollection
wiradd(Object o)
. Was wäre eine solche Umsetzung inMap
. Es macht keinen Sinn, eine solche Methode zu habenMap
. Stattdessen haben wir eineput(key,value)
Methode inMap
.Gleiche Argument gilt für
addAll()
,remove()
undremoveAll()
Methoden. Der Hauptgrund ist also der Unterschied in der ArtMap
und Weise, wie Daten in und gespeichert werdenCollection
. Auch wenn Sie sich an eineCollection
implementierteIterable
Schnittstelle erinnern.iterator()
, sollte jede Schnittstelle mit Methode einen Iterator zurückgeben, der es uns ermöglichen muss, über die in der gespeicherten Werte zu iterierenCollection
. Was würde eine solche Methode für eine zurückgebenMap
? Schlüsseliterator oder Wertiterator? Das macht auch keinen Sinn.Es gibt Möglichkeiten, wie wir Schlüssel- und Wertspeicher in a durchlaufen können, und so
Map
ist es Teil desCollection
Frameworks.quelle
There are ways in which we can iterate over keys and values stores in a Map and that is how it is a part of Collection framework.
Können Sie hierfür ein Codebeispiel zeigen?add(Object o)
wäre das Hinzufügen einesEntry<ClassA, ClassB>
Objekts. AMap
kann als a betrachtet werdenCollection of Tuples
Eigentlich, wenn es so wäre
implements Map<K,V>, Set<Map.Entry<K,V>>
, dann stimme ich eher zu. Es scheint sogar natürlich. Aber das funktioniert nicht sehr gut, oder? Nehmen wir anHashMap implements Map<K,V>, Set<Map.Entry<K,V>
, wir habenLinkedHashMap implements Map<K,V>, Set<Map.Entry<K,V>
usw. ... das ist alles gut, aber wenn Sie es hättenentrySet()
es getan haben, wird niemand vergessen, diese Methode zu implementieren, und Sie können sicher sein, dass Sie entrySet für jede Karte erhalten können, während Sie es nicht sind, wenn Sie hoffen dass der Implementierer beide Schnittstellen implementiert hat ...Der Grund, den ich nicht haben möchte,
interface Map<K,V> extends Set<Map.Entry<K,V>>
ist einfach, weil es mehr Methoden geben wird. Und es sind doch verschiedene Dinge, oder? Auch sehr praktisch, wenn ichmap.
in IDE drücke, will ich nicht sehen.remove(Object obj)
, und.remove(Map.Entry<K,V> entry)
weil ich damit nichthit ctrl+space, r, return
fertig werden kann.quelle
Map<K,V>
sollte nicht verlängernSet<Map.Entry<K,V>>
seit:Map.Entry
s mit demselben Schlüssel zum selben hinzufügenMap
, aberMap.Entry
s mit demselben Schlüssel zum selben hinzufügenSet<Map.Entry>
.quelle
Geradlinig und einfach. Die Sammlung ist eine Schnittstelle, die nur ein Objekt erwartet, während für die Karte zwei erforderlich sind.
quelle