The implementation of toList() clearly returns an ArrayList which does guarantee mutability:
public static <T>
Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>(ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}
but the JavaDoc of toList reads as:
/**
* Returns a {@code Collector} that accumulates the input elements into a
* new {@code List}. There are no guarantees on the type, mutability,
* serializability, or thread-safety of the {@code List} returned; if more
* control over the returned {@code List} is required, use {@link #toCollection(Supplier)}.
*
* @param <T> the type of the input elements
* @return a {@code Collector} which collects all the input elements into a
* {@code List}, in encounter order
*/
Why is mutability not guaranteed? Am I missing cases where the returned collection could be immutable? is it safe to use Collectors.toList() when mutability is required of the returned collection?
Why is mutability not guaranteed?
Because the designers of the Collectors.toList
method chose so.
The implementation of toList() clearly returns an ArrayList
That fact is a mere implementation detail.
And that detail applies only to the one version of the one implementation that you studied. Other versions, and other implementations, may use something other than ArrayList
.
Any implementation is subject to change, within the limits of the contract promised by the documentation. All that matters is the behavior promised by that documentation.
To quote the Javadoc:
Returns a
Collector
that accumulates the input elements into a newList
. There are no guarantees on the type, mutability, serializability, or thread-safety of theList
returned; if more control over the returned List is required, usetoCollection(Supplier)
.
Seems crystal clear to me. You have been promised that the resulting List
may be mutable or may be immutable. You’ve been told not to rely on either being the case.
If you want a mutable/modifiable List
, 👉🏽 make a new one from the list generated by your collector. Pass the collected list to the constructor of a new list object.
List< Whatever > list = new ArrayList<>( collectorList ) ;
Or, as the Javadoc above suggests, you can 👉🏽 pass Supplier
to render a list implementation of your choice. Call Collectors.toCollection
. See section 3.3 of the tutorial on Baeldung.com.
For example:
toCollection( ArrayList :: new )
If you want an immutable/unmodifiable list, I suggest 👉🏽 calling List.copyOf
.
List< Whatever > list = List.copyOf( collectorList ) ;
That method promises to return:
… an unmodifiable List containing the elements of the given
Collection
, in its iteration order.
But there is a catch: No nulls allowed. Two alternatives for a list with nulls:
Stream#toList
for an unmodifiable list tolerating nulls.Collections.unmodifiableList
.By the way, Java 21 brought sequenced collections. So the more general interface of List
is SequencedCollection
.
SequencedCollection< Whatever > sequence = List.copyOf( collectorList ) ;