专栏名称: 芋道源码
纯 Java 源码分享公众号,目前有「Dubbo」「SpringCloud」「Java 并发」「RocketMQ」「Sharding-JDBC」「MyCAT」「Elastic-Job」「SkyWalking」「Spring」等等
目录
相关文章推荐
芋道源码  ·  被问懵了,加密后的数据如何进行模糊查询? ·  8 小时前  
芋道源码  ·  Minio + Docker ... ·  8 小时前  
芋道源码  ·  java 插入式注解的打开方式! ·  昨天  
51好读  ›  专栏  ›  芋道源码

千万不要滥用Stream.toList(),有坑!

芋道源码  · 公众号  · Java  · 2025-02-23 16:40

正文

👉 这是一个或许对你有用 的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入 芋道快速开发平台 知识星球。 下面是星球提供的部分资料:

👉 这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号、CRM 等等功能:

  • Boot 仓库:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 仓库:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK 21 + SpringBoot 3.2.2、JDK 8 + Spring Boot 2.7.18 双版本

来源:juejin.cn/post/
7325717778597904420


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

1. 背景

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

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

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

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()

/**
 * 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.
 *
 * 

The returned instance may be @docRoot

}/java.base/java/lang/doc-files/ValueBased.html">value-based.
 * 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.
 *
 * 

This is a terminal operation.
 *
 * @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:
 * 

{@code
 * Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray())))
 * }

 *
 * @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 toList() {
    return (List) Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray())));
}

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

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

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

List stringList = List.of("1""2""3");
List largeNumberList = stringList.stream().filter(number -> Long.parseLong(number) > 1).toList();
List 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 userList = List.of(userZhang, userLi);
List 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的任何操作

/**
 * 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  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 
Collector> 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 说明

of和List.copyOf静态工厂方法为创建不可修改的列表提供了一种方便的方法。这些方法创建的List实例具有以下特征:

  • 它们是不可修改的。元素不能被添加、删除或替换。调用List上的任何mutator方法将始终导致引发 UnsupportedOperationException 。但是,如果包含的元素本身是可变的,这可能会导致List的内容看起来发生变化。
  • 它们不允许空元素。尝试使用空元素创建它们会导致 NullPointerException






请到「今天看啥」查看全文