Stream toList不能滥用以及与collect(Collectors.toList())的区别

Stream toList()返回的是只读List原则上不可修改,collect(Collectors.toList())默认返回的是ArrayList,可以增删改查

1. 背景

在公司看到开发环境突然发现了UnsupportedOperationException 报错,想到了不是自己throw的应该就是操作collection不当。

发现的确是同事使用了类似stringList.stream().filter(number -> Long.parseLong(number) > 1).toList() 以stream.toList()作为返回, 后继续使用了返回值做add操作,导致报错

2. Stream toList()collect(Collectors.toList())的区别

JDK version: 21

IDE: IDEA

从Java16开始,Stream有了直接toList方法 , java8时候常用的方法是 stringList.stream().filter(number -> Long.parseLong(number) > 1).collect(Collectors.toList())

Stream toList()

java 复制代码
    /**
     * 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())));
    }

查看源码 Stream toList调用的是Collections.unmodifiableList 而在unmodifiableList(List<? extends T> list)实现中,都会返回一个不可修改的List ,所以不能使用set/add/remove等改变list数组的方法

java 复制代码
 return (list instanceof RandomAccess ?
                new UnmodifiableRandomAccessList<>(list) :
                new UnmodifiableList<>(list));

但其实也可以修改List的元素的某些属性,例如

java 复制代码
        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返回的也是不能修改的List
        userList.forEach(u -> System.out.print(u.name));

输出结果是:

WangWu

ZhangSanWangWu

Stream collect(Collectors.toList())

返回一个ArrayList 如果没有在toList()方法入参中传入指定Supplier的话, 可以做ArrayList的任何操作

java 复制代码
    /**
     * 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(),返回的也是不可修改的list, an unmodifiable list. 关于 Unmodifiable Lists 说明

The List.of and List.copyOf static 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 subList views implement the RandomAccess interface.

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.如何使用(不考虑性能)

确定其是一个不再被set/add/remove的list 可使用 Stream toList;

如果使用collect(Collectors.toList()) ,sonar或idea自带以及第三方的一些code checker会爆warning, 以本人经验,可以使用collect(Collectors.toCollection(ArrayList::new))来代替

相关推荐
*小海豚*5 分钟前
在linux服务器上DNS正常,但是java应用调用第三方解析域名报错
java·linux·服务器
撩得Android一次心动20 分钟前
Android LiveData 全面解析:使用Java构建响应式UI【源码篇】
android·java·android jetpack·livedata
组合缺一24 分钟前
Solon AI (Java) v3.9 正式发布:全能 Skill 爆发,Agent 协作更专业!仍然支持 java8!
java·人工智能·ai·llm·agent·solon·mcp
MSTcheng.29 分钟前
【C++】C++11新特性(二)
java·开发语言·c++·c++11
一 乐32 分钟前
校园二手交易|基于springboot + vue校园二手交易系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端
KIKIiiiiiiii33 分钟前
微信个人号API二次开发中的解决经验
java·人工智能·python·微信
80530单词突击赢34 分钟前
SpringBoot整合SpringMVC全解析
java·spring boot·后端
vx1_Biye_Design44 分钟前
基于Spring Boot+Vue的学生管理系统设计与实现-计算机毕业设计源码46223
java·vue.js·spring boot·spring·eclipse·tomcat·maven
vx_Biye_Design1 小时前
基于Spring Boot+vue的湖北旅游景点门票预约平台的设计--毕设附源码29593
java·vue.js·spring boot·spring cloud·servlet·eclipse·课程设计
hay_lee1 小时前
Spring AI实现对话聊天-流式输出
java·人工智能·ollama·spring ai