JDK 16: Stream to List In One Easy Call
As Java functional streams have become increasingly popular, an increasing number of requests is being made for new stream operations to be supported. Amidst these requests for numerous disparate new operations, one operation that seems to be requested more than the others is an operation that directly provides a List from a Stream. JDK 16 Early Access Build 27 introduces Stream.toList()
, which is the subject of this post.
Before the JDK 16 Early Access Build 27 introduction of Stream.toList()
, the most common approach for acquiring a List
from a Stream
was to invoke the approprite Collector:
stream.collect(Collectors.toList())
This is not a lot of code and it’s fairly straightforward once you see it, but many have wanted an even more concise syntax for this frequently used stream operation. JDK 16 brings us this:
stream.toList()
It may be tempting to go into one’s code base and use stream.toList()
as a drop-in replacement for stream.collect(Collectors.toList())
, but there may be differences in behavior if the code has a direct or indirect dependency on the implementation of stream.collect(Collectors.toList())
returning an ArrayList. Some of the key differences between the List
returned by stream.collect(Collectors.toList())
and stream.toList()
are spelled out in the remainder of this post.
The Javadoc-based documentation for Collectors.toList() states (emphasis added), “Returns a Collector
that accumulates the input elements into a new List
. There are no guarantees on the type, mutability, serializability, or thread-safety of the List
returned…” Although there are no guarantees regarding the “type, mutability, serializability, or thread-safety” on the List
provided by Collectors.toList()
, it is expected that some may have realized it’s currently an ArrayList
and have used it in ways that depend on the characteristics of an ArrayList
.
The following code snippet (full code listing on GitHub) shows a method that can be executed against the List
implementations returned by Collectors.toList()
and Stream.toList()
to see what they have in common and how the are different.
/** * Analyzes the supplied {@code List} and writes to standard output * some key characteristics of the supplied {@code List}. * * @param listDescription Description of {@code List} to be analyzed. * @param listUnderAnalysis {@code List} to be analyzed. */ private static void analyzeList( final String listDescription, final List<String> listUnderAnalysis) { out.println(listDescription + ": "); out.println("\tClass Type: " + listUnderAnalysis.getClass().getCanonicalName()); out.println("\tAble to add to List? " + isListAddCapable(listUnderAnalysis)); out.println("\tAble to sort List? " + isListSortable(listUnderAnalysis)); }
When the simple analysis code above is executed against implementations of List
returned by Stream.collect(Collectors.toList())
and Stream.toList()
, the output appears as shown next.
Stream.collect(Collectors.toList()): Class Type: java.util.ArrayList Able to add to List? true Able to sort List? true Stream.toList(): Class Type: java.util.ImmutableCollections.ListN Able to add to List? false Able to sort List? false [NOT Stream] List.of(): Class Type: java.util.ImmutableCollections.ListN Able to add to List? false Able to sort List? false
The output shown above demonstrates that Stream.toList()
provides a List
implementation that is immutable (type ImmutableCollections.ListN
that cannot be added to or sorted) similar to that provided by List.of() and in contrast to the mutable (can be changed and sorted) ArrayList
provided by Stream.collect(Collectors.toList())
. Any existing code depending on the ability to mutate the ArrayList
returned by Stream.collect(Collectors.toList())
will not work with Stream.toList()
and an UnsupportedOperationException will be thrown.
Although the implementation nature of the List
s returned by Stream.collect(Collectors.toList())
and Stream.toList()
are very different, they still both implement the List interface and so they are considered equal when compared using List.equals(Object). This is demonstrated in the full code listing on GitHub.
The addition of method toList()
to the Stream interface is a small thing, but it does make an often-used technique more convenient.
Published on Java Code Geeks with permission by Dustin Marx, partner at our JCG program. See the original article here: JDK 16: Stream to List In One Easy Call Opinions expressed by Java Code Geeks contributors are their own. |
Ugh why make it immutable?