Can I cast collection to List?

Table of Contents

  • Introduction
  • 1 Collectors.toList
  • 2 Collectors.toCollection
  • 3 Collectors.toUnmodifiableList
    • null values not allowed
  • 4 forEach
  • 5 forEachOrdered
  • 6 Convert to an Array and then to List
    • Mutability of the returned list by Arrays.asList
  • Summary
  • Generic method to convert stream to a List
  • Conclusion
  • References

Introduction

I have written aboutJava streammethods extensively in the past. In this post, we will see six ways to convert a Java stream to a List. You can easily extend these if you want to convert a stream to a Set as well.

1 Collectors.toList

The first method is the most obvious and the easiest one. We use the Collectors class static methodtoList[]to get a collector and pass it to thecollectmethod on the stream. This collector accumulates the stream elements into a new List.

There are no guarantees on the type, the mutability, serializability or thread-safety of the list that is returned. It is animplementation detailthat it returns anArrayListtoday. It could change in the future.

Stream stream = Stream.of["Jon", "Adam", "Perry", "Monty"]; List names = stream .collect[Collectors.toList[]]; System.out.println[names];

Prints,

[Jon, Adam, Perry, Monty]

2 Collectors.toCollection

If we wanted more control over the type of list we collect the stream elements into, we can useCollectors.toCollection method. This method accepts a Supplier of a Collection type as an argument. The returned collector accumulates the stream elements into the collection returned by the supplier [that we pass].

For example, if we wanted the result list to be an ArrayList, we can pass a supplier as shown below

Stream stream = Stream.of["Jon", "Adam", "Perry", "Monty"]; List names = stream .collect[Collectors.toCollection[[] -> new ArrayList[]]]; System.out.println[names];

Using method reference, we can rewrite the above as

names = stream .collect[Collectors.toCollection[ArrayList::new]];

Similarly, if we wanted the result to be a LinkedList, we just change the supplier factory to create a new LinkedList instead. Hence, the result will be a LinkedList.

Stream stream = Stream.of["Jon", "Adam", "Perry", "Monty"]; List names = stream .collect[Collectors.toCollection[LinkedList::new]]; System.out.println[names];

3 Collectors.toUnmodifiableList

The Collectors class has a static method calledtoUnmodifiableListwhich returns a Collector that accumulates the stream elements into anunmodifiable listas per the encounter order.

Stream stream = Stream.of["Jon", "Adam", "Perry", "Monty"]; List names = stream .collect[Collectors.toUnmodifiableList[]]; System.out.println[names];

null values not allowed

Unlike the other Collector methods we have seen so far,toUnmodifiableListdoes not allow null values in the stream. It will throw a NullPointerException if there are null values in the stream.

4 forEach

In this rudimentary approach, we use forEach to iterate over the stream elements and add it to the result list. Hence, we pass the action to add to the list as a consumer.

There are some problems when dealing with parallel stream pipelines.

  1. There is no guarantee to preserve the encounter order of the stream. It could thus add to the list in any order.
  2. The result list we add to may behave in weird ways when added from multiple threads in a parallel stream.
Stream stream = Stream.of["Jon", "Adam", "Perry", "Monty"]; List names = new ArrayList[]; stream.forEach[names::add]; System.out.println[names];

ArrayList is not thread-safe but we are adding to it from multiple threads in the above code. Hence, we have to use a collection object that is thread-safe.

5 forEachOrdered

To overcome the problems in the previous method, we could use theforEachOrderedmethod.

This method obeys the encounter order of a stream even for parallel streams [thus it can affect performance]. As per Javadoc,

Performing the action for one element happens-before performing the action for subsequent elements, but for any given element, the action may be performed in whatever thread the library chooses.Stream stream = Stream.of["Jon", "Adam", "Perry", "Monty"]; List names = new ArrayList[]; stream.forEachOrdered[names::add]; System.out.println[names];

6 Convert to an Array and then to List

In this option, we first convert the stream to an array through thetoArraymethod. ThetoArraymethod accepts a generator function to allocate the result array.

Stream stream = Stream.of["Jon", "Adam", "Perry", "Monty"]; List names = Arrays.asList[stream .toArray[String[]::new]]; System.out.println[names];

The above shown generator function can also be written as size -> new String[size]. It takes an integer and allocates the result array of that size.

Then, we callArrays.asListmethod passing the array to get back a List. The Arrays#asList method acts as a bridge between array-based and collection-based APIs.

It returns a list which isbacked up by the passed array. Hence, changes made to the array will be visible in the list and vice-versa. Since it does not copy the array elements into a new list, the only additional thing is the call toasList.

Mutability of the returned list by Arrays.asList

The returned list is modifiable [supportssetmethod]. But it does not support adding to the list.

Any changes to the array will be reflected in the list. But the array created from the stream was an intermediate object. No references to it is available after wrapping it in Arrays.asList in the above example.

Summary

Options 1, 2 and 3 are the recommended ways to convert a Java stream to a List. Options 4 and 5 [forEach and forEachOrdered] are not recommended due to the discussed limitations. Option 6 [converting a stream to array and then to a list] is a roundabout way.

Generic method to convert stream to a List

You can make a generic method to convert a stream to a List.

Example: Option 1 can be written as a generic method as:

private static List collectorsToList[Stream stream] { return stream .collect[Collectors.toList[]]; }

In the same way you can turn any of the approaches we have seen into a generic method.

Wherever you want to convert a stream to a list, you can call thecollectorsToListhelper method passing the list. However, this logic is not that huge that warrants it to be an utility method.

Conclusion

In this post, we saw six ways to convert a Java Stream to a List. I recommend checking out the other posts on theJava stream.

References

Why is list.parallelStream[].forEach[] not processing all the elements in the list in Java

Video liên quan

Chủ Đề