Stream toList()returns a read-only List which cannot be modified in principle. collect(Collectors.toList())By default, it returns an ArrayList, which can be added, deleted, modified and searched.

1. Background

When I saw the development environment in the company, I suddenly noticed UnsupportedOperationExceptionan error. I thought that it was either me who threw it or improper operation of the collection.
I found that my colleague did use something like stringList.stream().filter(number -> Long.parseLong(number) > 1).toList()stream.toList() as a return, and then continued to use the return value for the add operation, resulting in an error.

2. The difference between Stream toList()andcollect(Collectors.toList())

JDK version: 21

IDE: IDEA

Starting from Java16, Stream has a direct toListmethod , which is the commonly used method in Java8 stringList.stream().filter(number -> Long.parseLong(number) > 1).collect(Collectors.toList()).

Stream toList()

    /**
     * Accumulates the elements of this stream into a {@code List}. The elements in
     * the list will be in this stream's encounter order, if one exists. The returned List
     * is unmodifiable; calls to any mutator method will always cause
     * {@code UnsupportedOperationException} to be thrown. There are no
     * guarantees on the implementation type or serializability of the returned List.
     *
     * <p>The returned instance may be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
     * Callers should make no assumptions about the identity of the returned instances.
     * Identity-sensitive operations on these instances (reference equality ({@code ==}),
     * identity hash code, and synchronization) are unreliable and should be avoided.
     *
     * <p>This is a <a href="package-summary.html#StreamOps">terminal operation</a>.
     *
     * @apiNote If more control over the returned object is required, use
     * {@link Collectors#toCollection(Supplier)}.
     *
     * @implSpec The implementation in this interface returns a List produced as if by the following:
     * <pre>{@code
     * Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray())))
     * }</pre>
     *
     * @implNote Most instances of Stream will override this method and provide an implementation
     * that is highly optimized compared to the implementation in this interface.
     *
     * @return a List containing the stream elements
     *
     * @since 16
     */
    @SuppressWarnings("unchecked")
    default List<T> toList() {
        return (List<T>) Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray())));
    }

View the source code. Stream toList is called. Collections.unmodifiableListIn unmodifiableList(List<? extends T> list)the implementation, an unmodifiable List will be returned , so methods such as set/add/remove cannot be used to change the list array .

 return (list instanceof RandomAccess ?
                new UnmodifiableRandomAccessList<>(list) :
                new UnmodifiableList<>(list));

But in fact, you can also modify some attributes of List elements, such as

        List<String> stringList = List.of("1", "2", "3");
        List<String> largeNumberList = stringList.stream().filter(number -> Long.parseLong(number) > 1).toList();
        List<String> largeNumberList2 = stringList.stream().filter(number -> Long.parseLong(number) > 1).collect(Collectors.toList());
//        largeNumberList.add("4"); //  java.lang.UnsupportedOperationException
        largeNumberList2.add("4"); //success

        // modify custom object attribute
        User userZhang = new User("ZhangSan");
        User userLi = new User("LiSi");
        List<User> userList = List.of(userZhang, userLi);
        List<User> filterList = userList.stream().filter(user -> "LiSi".equals(user.name)).toList();

        User first = filterList.getFirst();//java 21
        first.name = "WangWu";
        filterList.forEach(u -> System.out.println(u.name));
        //List.of
        userList.forEach(u -> System.out.print(u.name));

The output is:

WangWu

ZhangSanWangWu

Stream collect(Collectors.toList())

Returns a ArrayListIf toList()the specified Supplier is not passed in the method input parameter, any operation on the ArrayList can be performed

    /**
     * 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
     */
    public static <T>
    Collector<T, ?, List<T>> toList() {
        return new CollectorImpl<>(ArrayList::new, List::add,
                                   (left, right) -> { left.addAll(right); return left; },
                                   CH_ID);
    }

tips: List.of(), the returned list is also an unmodifiable list. About Unmodifiable Lists

The List.ofand List.copyOfstatic factory methods provide a convenient way to create unmodifiable lists. The List instances created by these methods have the following characteristics:
They are unmodifiable. Elements cannot be added, removed, or replaced. Calling any mutator method on the List will always cause UnsupportedOperationException to be thrown. However, if the contained elements are themselves mutable, this may cause the List’s contents to appear to change.
They disallow null elements. Attempts to create them with null elements result in NullPointerException.
They are serializable if all elements are serializable.
The order of elements in the list is the same as the order of the provided arguments, or of the elements in the provided array. The lists and their subListviews implement the RandomAccessinterface.
They are value-based. Programmers should treat instances that are equal as interchangeable and should not use them for synchronization, or unpredictable behavior may occur. For example, in a future release, synchronization may fail. Callers should make no assumptions about the identity of the returned instances. Factories are free to create new instances or reuse existing ones.
They are serialized as specified on the Serialized Form page.
This interface is a member of the Java Collections Framework.

3. How to use (regardless of performance)

Make sure it is a list that is no longer set/added/removed. You can use Stream toList;
if you use it collect(Collectors.toList()), some code checkers that come with sonar or idea and third parties will cause warnings. Based on my experience, you can use collect(Collectors.toCollection(ArrayList::new))it instead .

Leave a Reply

Your email address will not be published. Required fields are marked *