👉 这是一个或许对你有用的社群
🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入芋道快速开发平台知识星球。下面是星球提供的部分资料:
👉这是一个或许对你有用的开源项目
国产 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 双版本 

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.

 *

 * <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数组的方法。
return
 (list 
instanceof
 RandomAccess ?

new
 UnmodifiableRandomAccessList<>(list) :

new
 UnmodifiableList<>(list));

但其实也可以修改List的元素的某些属性,例如
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的任何操作
/**

 * 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

 */

publicstatic
 <T>

Collector<T, ?, List<T>> toList() {

returnnew
 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
  • 如果所有元素都是可序列化的,那么它们就是可序列化的。
  • 列表中元素的顺序与提供的参数或提供的数组中元素的顺序相同。列表及其子List视图实现了RandomAccess接口。
  • 它们以价值为基础。应将相等的实例视为可互换的,不应将它们用于同步,否则可能会发生不可预知的行为。如,在将来的版本中,同步可能会失败。呼叫端不应假设传回执行个体的识别。工厂可以自由地创建新的实例或重用现有的实例。
  • 它们将按照“序列化窗体”页上的指定进行序列化。
  • 该接口是Java集合框架的成员。
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

3.如何使用(不考虑性能)

确定其是一个不再被set/add/remove的list 可使用 Stream toList;
如果使用collect(Collectors.toList()),sonar或idea自带以及第三方的一些code checker会爆warning,以本人经验,可以使用collect(Collectors.toCollection(ArrayList::new))来代替

欢迎加入我的知识星球,全面提升技术能力。
👉 加入方式,长按”或“扫描”下方二维码噢
星球的内容包括:项目实战、面试招聘、源码解析、学习路线。
文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
继续阅读
阅读原文