Neue Factory Methoden für Collections

Java 9 Bootcamp Logo

JEP 269: "Convenience Factory Methods for Collections"

Java wird zu Recht oft Geschwätzigkeit vorgeworfen. In anderen Sprachen ist häufig deutlich weniger Code nötig um dasselbe zu erreichen. Das liegt einerseits an der Syntax der Sprache, andererseits an uneleganten APIs. Die Collections API sind das beste Beispiel. Das JEP 269 will diesen Umstand durch praktische Factory Methoden verbessern.

Motivation

Um ein unmodifizierbares Set zu Erzeugen und zu Befüllen benötigen wir in Java 8 eine Menge Code:

Set<String> set = new HashSet<>();
set.add("a");
set.add("b");
set.add("c");
set = Collections.unmodifiableSet(set);

Das hat in diesem Fall nichts mit der Java Syntax zu tun. Hier wurde einfach nicht genug für die einfache Benutzbarkeit der API getan. Es gibt zwar einige Tricks, wie man das mit weniger Code schaffen kann, diese sind aber teilweise sehr obskur und tragen sicher nicht zur Lesbarkeit bei. In der Spezifikation von JEP 269 werden einige Beispiele dazu gezeigt. Dieses hier möchte ich Euch nicht vorenthalten:

Set<String> set = Collections.unmodifiableSet(new HashSet<String>() {{
    add("a"); add("b"); add("c");
}});

Hier wird eine innere Klasse erzeugt und der Instance Initializer (doppelte geschweifte Klammern) verwendet, um die Werte hinzuzufügen. Schlau, aber nicht wirklich schön, oder? Da ist dieses Lösungsbeispiel schon besser nachzuvollziehen:

Set<String> set = Collections.unmodifiableSet(Stream.of("a", "b", "c").collect(toSet()));

Aber auch hier gibt es unnötige Berechnungen und Objekterzeugung.

Wie geht das in Java 9?

Für die Vereinfachung dieser Initialisierung wurden zwei Möglichkeiten diskutiert, Spracherweiterung im Rahmen von Project Coin, oder eine Library-basierte Lösung. Die Expert Group hat sich schliesslich für die weniger aufwändige Library-Erweiterung entschieden. Aber auch damit wird die Ezeugung deutlich schöner:

Set<String> set = Set.of("a", "b", "c");
List<String> list = List.of("a", "b", "c");

Das sieht zunächst sehr einfach aus, bietet aber auch unter der Haube die Möglichkeit zur Optimierung. So können zum Beispiel für kleine Sets optimierte Collections erzeugt werden, welche die Performance und den Speicherverbrauch optimieren. Da eine gute Performance von Collections für viele Java Anwendungen extrem wichtig ist, wurde hier auch auf Details geachtet:

Die Factory Methoden haben zwar einen Varargs-Konstruktor, dieser wird aber erst ab 10 Elementen verwendet, weil bei der Instanziierung über einen Varargs-Konstruktor immer ein Array erzeugt werden muss. Deshalb gibt es jeweils 11 überladene "of"-Factory-Methoden für 0 - 10 Elemente, und eine Methode "ofEntries" mit Varargs-Argument.

Was bedeutet das für Performance und Speicherbedarf?

Was die Performance und den Speicherbedarf zur Laufzeit angeht kann besonders bei kleinen Maps eine Menge optimiert werden. Wenn wir, wie in unserem ersten Beispiel ein HashSet wrappen brauchen wir einen Wrapper, und ein HashSet-Objekt mit all seinen Members, eine HashMap, ein Array als Bucket und eine Node Instanz pro Element.

Die neue Factory-Methode kann hingegen ein viel einfacher implementiertes Set zurückgeben, das einfach auf Feldern oder Arrays basiert. Die Methodenaufrufe müssen ebenfalls nicht delegiert werden, was die Ausführung zusätzlich beschleunigt und die Beschränkung auf nicht-modifizierbare Collections vereinfacht die Implementierung noch mehr.

Generell bieten die factory Methoden auch mehr Möglichkeiten zur Weiterentwicklung, da nicht festgelegt ist, welche konkrete Klasse zurückgegeben wird. So kann in Zukunft jederzeit weiter optimiert werden.

Fazit

Im Vorfeld gab es zwar den Wunsch Spracherweiterungen für die Initialisierung von Collections einzuführen, dies wurde aber zugunsten dieses Library-basierten Ansatzes verworfen, beziehungsweise auf die Zeit nach der Einführung von Value Types verschoben. Davon abgesehen wird JEP 269 selbst vermutlich nicht zu vielen Diskussionen führen. Es bietet praktische Erleichterungen beim Erzeugen von Collections ohne die Lesbarkeit von Code zu beeinträchtigen. Zusätzlich bietet die API eine einfache Möglichkeit zum Performancegewinn. Die performanten "Mikro-Collections" können auch in den Java Core APIs selbst eingesetzt werden, und haben das Potenzial Java diese weiter zu beschleunigen, selbst wenn man auf den direkten Einsatz verzichtet.

Besuchen Sie unsere Workshops zum Thema Java 9 um mehr über dieses und weitere neue Features von Java 9 zu erfahren: