Collections.emptyMap () gegen neue HashMap ()

143

Was sind einige der Situationen, in denen ich verwenden kann Collections.emptyMap()? In der Dokumentation heißt es, dass ich diese Methode verwenden kann, wenn meine Sammlung unveränderlich sein soll.

Warum sollte ich eine unveränderliche leere Sammlung wollen? Was ist der Sinn?

Vinoth Kumar CM
quelle
12
+1 auf die Frage, weil ich diese statische Methode noch nie gesehen habe
Tim Bender
Vielleicht sehen Sie sich auch Scala-Objektive an. Google Scala-Objektive . Mit der emptyMap und der immutableMap können Objekte erstellt werden, die unveränderlich sind. emptyMap ist der Ausgangspunkt. Jedes Mal, wenn ein Element hinzugefügt wird, wird die Karte selbst durch eine Karte ersetzt, die das alte Element und die neuen Elemente enthält. Der Punkt ist, dass Zugriffe auf das Objekt sicher sind.
michael_s
1
Es ist wie bei Objective-C [NSArray array], es gibt ein Objekt zurück, das zwar nicht verwendbar ist, aber existiert. Sie können also wie ein normales Objekt damit spielen und erhalten keinen Fehler.
Shane Hsu

Antworten:

144

Von Effective Java , Artikel # 43 - "Return empty arrays or collections, not null"zeigt eine leere Sammlung zurückkehren und vielleicht sogar demonstriert mit diesen emptyList(), emptySet()und emptyMap()Methoden auf die Klasse Collections eine leere Sammlung zu erhalten , die auch den zusätzlichen Vorteil, dass sie unveränderlich ist. Ab Punkt 15 "Minimize Mutability" .

Aus Collections-emptySet-Collections-emptyList-Collections

Es ist eine Art Programmiersprache. Dies ist für Personen gedacht, die keine Nullvariablen möchten. Bevor das Set initialisiert wird, können sie das leere Set verwenden.

Hinweis: Der folgende Code ist nur ein Beispiel (ändern Sie ihn entsprechend Ihrem Anwendungsfall):

private Set myset = Collections.emptySet();

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

Diese Methoden bieten einige Vorteile:

  1. Sie sind prägnanter, da Sie den generischen Typ der Sammlung nicht explizit eingeben müssen - er wird im Allgemeinen nur aus dem Kontext des Methodenaufrufs abgeleitet.

  2. Sie sind effizienter, weil sie sich nicht die Mühe machen, neue Objekte zu erstellen. Sie verwenden lediglich ein vorhandenes leeres und unveränderliches Objekt wieder. Dieser Effekt ist im Allgemeinen sehr gering, aber gelegentlich (gut, selten) wichtig.

Sumit Singh
quelle
15
+1 für die effektive Java-Referenz. Ein Nachteil: Ich würde die Parametrisierung Setund HashSetin Ihren Beispielen empfehlen , da der springende Punkt der emptySet()Methode und der Freunde (im Gegensatz zu den Konstanten Collections.EMPTY_SETusw.) darin besteht, dass sie gut mit Generika spielen. Außerdem ist die Verwendung einer Funktion (Rohtypen), die seit Java 5 veraltet ist, keine gute Lehrhilfe.
Daniel Pryden
4
Ich bin nach wie vor nicht überzeugt ... Ist es nicht sinnvoll, ein Collectionstatt nullzu verwenden, um Exceptionsnicht auf folgende Operationen geworfen zu werden? Die Verwendung einer unveränderlichen Sammlung würde nur zu einer anderen Ausnahme führen, die ich mir vorstelle. Und das Zuweisen nullist mit Sicherheit nicht weniger effizient als das Zuweisen einer unveränderlichen Konstante.
Fgysin wieder Monica
14
@fgysin: Wenn Sie eine API haben, bei der von Clients erwartet wird, dass sie die Sammlung ändern, ist es nicht sinnvoll, eine unveränderliche Sammlung zurückzugeben. Wenn Sie jedoch über eine API verfügen, in der Sie eine Sammlung zurückgeben, die von Clients nicht geändert werden darf und nur iteriert werden soll, ist die Rückgabe einer Ansicht an die zugrunde liegende Sammlung absolut sinnvoll, um sicherzustellen, dass ein "schlechter" Client nicht versehentlich Sammlungen ändert, deren Eigentümer er ist von dir. Wenn Sie eine leere Sammlung anstelle von null zurückgeben, muss Ihr Client vor der Verwendung der Sammlung keine Nullprüfung durchführen, um den Clientcode klarer zu gestalten.
Jean Hominal
4
public boolean setExists() { return !myset.equals(Collections.emptySet()); }
Assylias
5
In Ihrem Beispiel suchen Sie explizit nach der leeren Menge und verwenden sie als Sentinel-Wert. Ihre Klasse ist veränderbar. In Ihrem kleinen Beispiel werden Sentinels und Veränderlichkeit verwendet. Genau das versucht Collections.emptySet () zu verhindern.
Marc O'Morain
33

Nach meiner persönlichen Erfahrung ist dies zugegebenermaßen sehr nützlich, wenn eine API eine Sammlung von Parametern erfordert, Sie jedoch nichts bereitzustellen haben. Beispielsweise haben Sie möglicherweise eine API, die ungefähr so ​​aussieht und keine Nullreferenzen zulässt:

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

Wenn Sie eine Abfrage haben, die keine Parameter akzeptiert, ist es sicherlich etwas verschwenderisch, eine HashMap zu erstellen, bei der ein Array zugewiesen wird, wenn Sie einfach die "leere Karte" übergeben können, die praktisch eine Konstante ist, wie sie implementiert ist in java.util.Collections.

Affe
quelle
22

Warum sollte ich eine unveränderliche leere Sammlung wollen? Was ist der Sinn?

Hier gibt es zwei verschiedene Konzepte, die zusammen betrachtet seltsam erscheinen. Es ist sinnvoller, wenn Sie die beiden Konzepte getrennt behandeln.

  • Erstens sollten Sie nach Möglichkeit lieber eine unveränderliche als eine veränderbare Sammlung verwenden. Die Vorteile der Unbeweglichkeit sind an anderer Stelle gut dokumentiert .

  • Zweitens sollten Sie lieber eine leere Sammlung verwenden, als null als Sentinel zu verwenden. Dies ist hier gut beschrieben . Dies bedeutet, dass Sie viel saubereren und leichter verständlichen Code haben und weniger Stellen, an denen sich Fehler verstecken können.

Wenn Sie also Code haben, für den eine Karte erforderlich ist, ist es besser, eine leere Karte als eine Null zu übergeben, um das Fehlen einer Karte anzuzeigen. Wenn Sie eine Karte verwenden, ist es meistens besser, eine unveränderliche Karte zu verwenden. Aus diesem Grund gibt es eine praktische Funktion, um eine unveränderliche leere Karte zu erstellen.

Marc O'Morain
quelle
8

Es gibt einige Fälle, in denen Sie unveränderliche Karten, Listen, Mengen oder andere Arten von Sammlungen bevorzugen.

Der erste und wohl wichtigste Anwendungsfall ist, wenn Sie ein Ergebnis einer Abfrage oder einer Berechnung zurückgeben, die eine Menge (oder Liste oder Karte) von Ergebnissen zurückgeben würde, die Verwendung unveränderlicher Datenstrukturen vorzuziehen.

In diesem Fall ziehe ich es vor, unveränderliche Versionen davon zurückzugeben, da dies die tatsächliche Unveränderlichkeit einer Ergebnismenge einer Berechnung viel deutlicher widerspiegelt - unabhängig davon, was Sie später mit den Daten tun, sollte die Menge der Ergebnisse, die Sie von Ihrer Abfrage erhalten haben, dies nicht tun Veränderung.

Der zweite häufige Anwendungsfall ist, wenn Sie ein Argument als Eingabe für eine Methode oder einen Dienst angeben müssen. Es sei denn, Sie erwarten die Eingabesammlung durch den Dienst oder die Methode geändert wird (was normalerweise eine wirklich schlechte Entwurfsidee ist), kann die Übergabe einer unveränderlichen Sammlung anstelle der veränderlichen in vielen Fällen die vernünftige und sichere Wahl sein.

Ich betrachte es als "Pass by Value" -Konvention.

Allgemeiner gesagt - es ist sinnvoll, unveränderliche Datenstrukturen zu verwenden, wenn Daten Modul- oder Dienstgrenzen überschreiten. Dies macht es viel einfacher, über Unterschiede zwischen (unveränderlichem) Input / Output und veränderlichem internen Zustand nachzudenken.

Als sehr vorteilhafter Nebeneffekt ist dies eine erhöhte Sicherheit und Thread-Sicherheit Ihrer Module / Dienste und sorgt für eine sauberere Trennung von Bedenken.

Ein weiterer guter Grund für die Anwendung von Collections.empty*()Methoden ist der spürbare Mangel an Ausführlichkeit. In der Zeit vor Java7 mussten Sie, wenn Sie eine generische Sammlung hatten, generische Typanmerkungen überall verteilen.

Vergleichen Sie einfach diese beiden Erklärungen:

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

gegen:

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

Letzteres gewinnt auf zwei wichtige Arten zweifellos die Lesbarkeit:

  1. In der ersten Deklaration ist die gesamte Instanziierung einer leeren Karte im Rauschen generischer Typdeklarationen vergraben, wodurch eine im Wesentlichen triviale Deklaration viel kryptischer wird, als sie sein muss.
  2. Zusätzlich zum bemerkenswerten Fehlen einer generischen Typanmerkung auf der rechten Seite wird in der zweiten Version eindeutig angegeben, dass die Karte mit einer leeren Karte initialisiert wird. Da ich weiß, dass diese Methode eine unveränderliche Karte zurückgibt, ist es für mich jetzt einfacher zu finden, wo fooBarMapein anderer nicht leerer Wert zugewiesen wird, indem ich nur nach suche /fooBarMap =/.
Roland Tepp
quelle
5

Zum einen können Sie mit Referenzfreigabe davonkommen. Für ein new HashMap()etc sind ein zugewiesenes Objekt und möglicherweise einige zusätzliche Elemente erforderlich, um die Daten zu speichern. Sie benötigen jedoch nur eine Kopie einer unveränderlichen leeren Sammlung (Liste, Satz, Karte oder eine andere solche). Dies macht es zu einer offensichtlichen Wahl, wenn eine von Ihnen aufgerufene Methode eine Map akzeptieren, aber nicht bearbeiten muss.

Ich schlage vor, Josh Blochs Effective Java zu testen , in dem einige sehr schöne Attribute unveränderlicher Objekte (einschließlich Thread-Sicherheit) aufgeführt sind.

Jeff Bowman
quelle
3

Dies kann nützlich sein, wenn Sie eine Funktion haben, die eine zurückgibt, immutable collectionund in einigen Situationen keine Daten zurückgegeben werden müssen, sodass Sie anstelle einer Rückgabe zurückkehren nullkönnenemptyMap()

Es macht Ihren Code einfacher und verhindert NullPointerException

iTech
quelle
3

Meistens verwenden wir a constructor, um ein neues zu erstellen empty map. Aber das Collections methodsbietet ein paar Vorteile, um eine empty mapNutzung zu schaffenstatic method java.util.Collections.emptyMap()

  1. Sie sind prägnanter, da Sie den generischen Typ der Sammlung nicht explizit eingeben müssen - er wird im Allgemeinen nur aus dem Kontext des Methodenaufrufs abgeleitet.

  2. Sie sind effizienter, weil sie sich nicht die Mühe machen, neue Objekte zu erstellen. Sie verwenden lediglich ein vorhandenes leeres und unveränderliches Objekt wieder. Dieser Effekt ist im Allgemeinen sehr gering, aber gelegentlich (gut, selten) wichtig.

subodh
quelle
2

Warum sollte ich eine unveränderliche leere Sammlung wollen? Was ist der Sinn?

Aus dem gleichen Grund würden Sie Collections.unmodifiableMap()irgendwann verwenden. Sie möchten eine Map-Instanz zurückgeben, die eine Ausnahme auslöst, wenn der Benutzer versucht, sie zu ändern. Es ist nur ein Sonderfall: die leere Karte.

Ryan Stewart
quelle
1

Warum sollte ich eine unveränderliche leere Sammlung wollen? Was ist der Sinn?

Aus den gleichen Gründen, warum Sie möglicherweise unveränderliche Objekte möchten. In erster Linie, weil Sie nachts sicher schlafen können, wenn Sie wissen, dass mehrere Threads auf dieselbe Instanz eines Objekts zugreifen können und alle dieselben Werte sehen. Wenn Sie keine Elemente in einer Sammlung haben, ist dies immer noch ein gültiger Wert, den Sie beibehalten möchten.

vegemite4me
quelle