Java 16: toList() or not toList()

21.04.2021

Etwas über einen Monat ist die Veröffentlichung von Java 16 mittlerweile her. Wie bereits berichtet, wurde die Klasse java.util.stream.Stream um eine neue Methode toList() erweitert, die man nun direkt auf einen Stream aufrufen kann, ohne den bisher nötigen „Umweg“ über Stream.collect(Collectors.toList()). Die Implementierung von Stream.toList() weicht allerdings von der aus der Collectors-Klasse ab, was im Netz zu einigen Diskussionen geführt hat.

Was stimmt nicht mit toList()?

Donald Raab, Urheber der „Eclipse Collections“ bemängelt in einem Blogbeitrag unter anderem, dass die neue Methode Stream.toList() eine unveränderliche Listenimplementierung zurückliefert, die es nicht zulässt, Listenelemente hinzuzufügen, zu entfernen oder zu ersetzen. Collectors.toList() liefert dagegen eine java.util.ArrayList, bei der all diese Aktionen möglich sind. Als Argument führt Raab an, dass Nutzer in eine Falle tappen könnten, wenn sie aufgrund desselben Methodennamens auch bei Stream.toList() eine modifizierbare Liste erwarten würden, wofür sie zur Laufzeit eine UnsupportedOperationException ernten könnten. Außerdem gibt seit Java 10 die Methode Collectors.toUnmodifiableList(), die quasi dasselbe macht wie Stream.toList().Vor der Veröffentlichung von Java 16 hatte Raab daher in der OpenJDK-Mailingliste vorgeschlagen, die neue Methode aus Gründen der Konsistenz nach Stream.toUnmodifiableList() umzubenennen. Zudem stellte er die Frage, wieso es so lange gedauert hat, bis die „Bequemlichkeitsmethode“ toList() realisiert wurde. Seit Einführung der Streams in Java 8 sind mittlerweile immerhin stolze 7 Jahre ins Land gegangen.

Weiterhin wollte Raab wissen, ob es auch möglich wäre, die Methoden toSet() und toMap() in Stream zu ergänzen, die ebenfalls häufig benötigt werden.

Die Motivation

Auf die Frage, weshalb es mit Stream.toList() so lange gedauert hat, antwortete Brian Goetz, Java Language Architect bei Oracle, dass die Macher des JDK unnötige Methoden vermeiden wollen und daher sehr sparsam damit sind, um die APIs nicht zu überfrachten. Schließlich ist es weit einfacher, später neue Methoden hinzuzufügen, als bereits existierende wieder zu entfernen. Zudem wollte man eine zu starke Kopplung von Stream an Collections vermeiden. Da toList() die mit Abstand am häufigsten genutzte terminierende Operation von Streams ist, wurde nun dafür eine Ausnahme gemacht. Ein weiterer Grund dafür ist, dass die Implementierung im Kern der Stream-Klasse performanter ist als außerhalb.

Die Frage, weshalb Stream.toList() eine unmodifizierbare Liste zurückgibt beantwortet Goetz damit, dass dies am ehesten den Prinzipien der funktionalen Programmierung entspräche, welche auch als Grundlage für die Streams dienen. Dies sei auch die ursprüngliche Intention bei Collectors.toList() gewesen. Im Javadoc der Methode steht daher, dass keinerlei Annahmen über die (Un)Veränderlichkeit der zurückgelieferten Liste getroffen werden sollen. Eine veränderliche java.util.ArrayList wird nur deswegen zurückgegeben, weil es zum Zeitpunkt der Einführung von Streams noch keine geeignete, nicht veränderliche Listenemplementierung gegeben habe. Es sei sogar möglich, dass die von Collectors.toList() zurückgegebene Liste in einer späteren Java-Version tatsächlich unveränderlich sein könnte.

Die Frage der Konsistenz

Mit dem Hinweis auf das Javadoc entkräftet Goetz auch Raabs Aussage, toUnmodifiableList() sei ein besserer Methodenname. Denn wer bei toList() eine veränderliche Liste erwartet, würde den im Javadoc beschriebenen Kontrakt der Methode ignorieren, welcher besagt, dass man über die Modifizierbarkeit der Liste keine Annahmen treffen soll. Anwender sollten also besser davon ausgehen, dass die von toList() gelieferten Implementierungen unveränderlich seien und falls sie etwas anderes benötigen, sich mit Collectors.toCollection(…) behelfen, bei der sie die verwendete Listenimplementierung selbst bestimmen können.

Goetz schließt mit den Worten, dass zumindest eine scheinbare „Inkonsistenz“ bzgl. der Namen toList() und toUnmodifiableList() bestehe, da Anwender zu dem Schluss gekommen seien, toList() würde toArrayList() bedeuten (obwohl die Spezifikation etwas anderes sagt) und es Fälle gäbe, bei dem dies nicht der Fall sei. Er könne jedoch damit leben, da Konsistenz zwar eine gute Richtlinie sei, aber nicht zu einem törichten Extrem ausarten sollte.

 

Mehr zu Java Programmierungen erfahren

Zurück zur Übersicht

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

*Pflichtfelder

*