Java 9 Convenience Factory-Methoden für Sammlungen

Java 9 Convenience Factory-Methoden für Sammlungen

1. Überblick

Java 9 bringt den lang erwarteten syntaktischen Zucker zum Erstellen kleiner nicht modifizierbarerCollection-Instanzens mit einem präzisen einzeiligen Code. GemäßJEP 269 werden in JDK 9 neue Convenience-Factory-Methoden enthalten sein.

In diesem Artikel behandeln wir die Verwendung zusammen mit den Implementierungsdetails.

2. Geschichte und Motivation

Das Erstellen eines kleinen unveränderlichenCollection in Java ist auf herkömmliche Weise sehr ausführlich.

Nehmen wir ein Beispiel für einSet:

Set set = new HashSet<>();
set.add("foo");
set.add("bar");
set.add("baz");
set = Collections.unmodifiableSet(set);

Das ist viel zu viel Code für eine einfache Aufgabe und sollte in einem einzigen Ausdruck möglich sein.

Dies gilt auch fürMap. FürList gibt es jedoch eine Factory-Methode:

List list = Arrays.asList("foo", "bar", "baz");

Obwohl dieseList-Erstellung besser ist als die Konstruktorinitialisierung, ist diesless obvious, da die übliche Intuition nicht darin besteht, in derArrays-Klasse nach Methoden zum Erstellen vonList zu suchen:

Es gibt andere Möglichkeiten, die Ausführlichkeit zu reduzieren, wie diedouble-brace-Technik:

Set set = Collections.unmodifiableSet(new HashSet() {{
    add("foo"); add("bar"); add("baz");
}});

oder mit Java 8Streams:

Stream.of("foo", "bar", "baz")
  .collect(collectingAndThen(toSet(), Collections::unmodifiableSet));

Die Doppelklammer-Technik ist nur ein wenig weniger ausführlich, verringert jedoch die Lesbarkeit erheblich (und wird als Anti-Muster angesehen).

Die Java 8-Version ist jedoch ein einzeiliger Ausdruck und weist ebenfalls einige Probleme auf. Erstens ist es nicht offensichtlich und intuitiv, zweitens ist es immer noch ausführlich, drittens werden unnötige Objekte erstellt, und viertens kann diese Methode nicht zum Erstellen vonMapverwendet werden.

Um die Mängel zusammenzufassen, behandelt keiner der oben genannten Ansätze den spezifischen Anwendungsfall, wodurch ein kleines, nicht veränderbares erstklassiges Problem vonCollectionentsteht.

3. Beschreibung und Verwendung

Für die SchnittstellenList,Set undMap wurden statische Methoden bereitgestellt, die die Elemente als Argumente verwenden und eine Instanz vonList,Set undMap jeweils.

Diese Methode heißtof(…) für alle drei Schnittstellen.

3.1. List undSet

Die Signatur und Eigenschaften der Factory-Methoden vonList undSet sind identisch:

static  List of(E e1, E e2, E e3)
static  Set  of(E e1, E e2, E e3)

Anwendung der Methoden:

List list = List.of("foo", "bar", "baz");
Set set = Set.of("foo", "bar", "baz");

Wie Sie sehen, ist es sehr einfach, kurz und prägnant.

In diesem Beispiel haben wir die Methode verwendet, bei der genau drei Elemente als Parameter verwendet werden undList /Set der Größe 3 zurückgegeben werden.

Es gibt jedoch 12 überladene Versionen dieser Methode - elf mit 0 bis 10 Parametern und eine mit var-args:

static  List of()
static  List of(E e1)
static  List of(E e1, E e2)
// ....and so on

static  List of(E... elems)

Für die meisten praktischen Zwecke wären 10 Elemente ausreichend, aber wenn mehr erforderlich sind, kann die var-args-Version verwendet werden.

Nun können Sie sich fragen, wozu 11 zusätzliche Methoden sinnvoll sind, wenn es eine var-args-Version gibt, die für eine beliebige Anzahl von Elementen geeignet ist.

Die Antwort darauf ist Leistung. Every var-args method call implicitly creates an array. Having the overloaded methods avoid unnecessary object creation and the garbage collection overhead thereof.

Wenn beim Erstellen vonSet mithilfe einer Factory-Methode doppelte Elemente als Parameter übergeben werden, wirdIllegalArgumentException zur Laufzeit ausgelöst:

@Test(expected = IllegalArgumentException.class)
public void onDuplicateElem_IfIllegalArgExp_thenSuccess() {
    Set.of("foo", "bar", "baz", "foo");
}

Ein wichtiger Punkt hierbei ist, dass primitive Typen autoboxed werden, da die Factory-Methoden Generika verwenden.

Wenn ein Array vom primitiven Typ übergeben wird, wird einList vonarray dieses primitiven Typs zurückgegeben.

Zum Beispiel:

int[] arr = { 1, 2, 3, 4, 5 };
List list = List.of(arr);

In diesem Fall wird einList<int[]> der Größe 1 zurückgegeben und das Element am Index 0 enthält das Array.

3.2. Map

Die Signatur der Factory-Methode vonMaplautet:

static  Map of(K k1, V v1, K k2, V v2, K k3, V v3)

und die Verwendung:

Map map = Map.of("foo", "a", "bar", "b", "baz", "c");

Ähnlich wie beiList undSet ist die Methodeof(…) überladen, um 0 bis 10 Schlüssel-Wert-Paare zu haben.

Im Fall vonMap gibt es eine andere Methode für mehr als 10 Schlüssel-Wert-Paare:

static  Map ofEntries(Map.Entry... entries)

und es wird verwendet:

Map map = Map.ofEntries(
  new AbstractMap.SimpleEntry<>("foo", "a"),
  new AbstractMap.SimpleEntry<>("bar", "b"),
  new AbstractMap.SimpleEntry<>("baz", "c"));

Die Übergabe doppelter Werte für Key würde einIllegalArgumentException auslösen:

@Test(expected = IllegalArgumentException.class)
public void givenDuplicateKeys_ifIllegalArgExp_thenSuccess() {
    Map.of("foo", "a", "foo", "b");
}

Auch im Fall vonMap sind die primitiven Typen autoboxed.

4. Implementierungshinweise

Die mit den Factory-Methoden erstellten Sammlungen sind keine häufig verwendeten Implementierungen.

Zum Beispiel istList keinArrayList undMap ist keinHashMap. Dies sind verschiedene Implementierungen, die in Java 9 eingeführt wurden. Diese Implementierungen sind intern und ihre Konstruktoren haben eingeschränkten Zugriff.

In diesem Abschnitt werden einige wichtige Implementierungsunterschiede aufgeführt, die allen drei Arten von Sammlungen gemeinsam sind.

4.1. Unveränderlich

Die mit Factory-Methoden erstellten Sammlungen sind unveränderlich. Wenn Sie ein Element ändern, neue Elemente hinzufügen oder ein Element entfernen, werdenUnsupportedOperationException ausgelöst:

@Test(expected = UnsupportedOperationException.class)
public void onElemAdd_ifUnSupportedOpExpnThrown_thenSuccess() {
    Set set = Set.of("foo", "bar");
    set.add("baz");
}
@Test(expected = UnsupportedOperationException.class)
public void onElemModify_ifUnSupportedOpExpnThrown_thenSuccess() {
    List list = List.of("foo", "bar");
    list.set(0, "baz");
}
@Test(expected = UnsupportedOperationException.class)
public void onElemRemove_ifUnSupportedOpExpnThrown_thenSuccess() {
    Map map = Map.of("foo", "a", "bar", "b");
    map.remove("foo");
}

4.2. Keinnull Element erlaubt

Im Fall vonList undSet dürfen keine Elementenull sein. Im Fall vonMap können weder Schlüssel noch Wertenull sein. Wenn Sie das Argumentnullübergeben, wirdNullPointerException ausgelöst:

@Test(expected = NullPointerException.class)
public void onNullElem_ifNullPtrExpnThrown_thenSuccess() {
    List.of("foo", "bar", null);
}

4.3. Wertbasierte Instanzen

Die durch Factory-Methoden erstellten Instanzen sind wertebasiert. Dies bedeutet, dass Fabriken eine neue Instanz erstellen oder eine vorhandene Instanz zurückgeben können.

Wenn wir also Listen mit denselben Werten erstellen, verweisen diese möglicherweise auf dasselbe Objekt auf dem Heap:

List list1 = List.of("foo", "bar");
List list2 = List.of("foo", "bar");

In diesem Fall kannlist1 == list2 abhängig von der JVM zutrue ausgewertet werden oder nicht.

4.4. Serialisierung

Mit Factory-Methoden erstellte Sammlungen sindSerializable, wenn die Elemente der SammlungSerializable. sind

5. Fazit

In diesem Artikel stellten wir die neuen Factory-Methoden für Collections vor, die in Java 9 eingeführt wurden.

Wir haben festgestellt, warum diese Funktion eine willkommene Änderung darstellt, indem wir einige Methoden zur Erstellung nicht modifizierbarer Sammlungen aus der Vergangenheit durchgesehen haben. Wir haben die Verwendung behandelt und wichtige Punkte hervorgehoben, die bei der Verwendung berücksichtigt werden müssen.

Schließlich haben wir klargestellt, dass sich diese Sammlungen von den häufig verwendeten Implementierungen unterscheiden, und auf wichtige Unterschiede hingewiesen.

Der vollständige Quellcode für diesen Artikel und die Komponententests sindavailable over on GitHub.