从Stream的 toList() 和 collect(Collectors.toList()) 方法看Java的不可变流

环境

  • JDK 21
  • Windows 11 专业版
  • IntelliJ IDEA 2024.1.6

背景

在使用Java的Stream的时候,常常会把流收集为List。

假设有List list1 如下:

        var list1 = List.of("aaa", "bbbbbb", "cccc", "d", "eeeee");

要找到所有长度大于3的字符串。

Java 8的做法是:

        var list2 = list1.stream().filter(e -> e.length() > 3).collect(Collectors.toList());

但是IDEA会给出一个提示:

‘collect(toList())’ can be replaced with ‘toList()’

如下图所示:

在这里插入图片描述
可见,代码可以简化如下:

        var list3 = list1.stream().filter(e -> e.length() > 3).toList();

注意: toList() 方法是Java 16引入的。

区别

虽然 collect(Collectors.toList())toList() 方法都返回List,但是二者是有一些差异的。

前者返回的一般是一个ArrayList,是可以修改的,而后者返回的是一个不可修改的List。

如下图所示:

在这里插入图片描述

可见,如果尝试给 list3 添加元素,IDEA会提示:

Immutable object is modified

注意:编译并不会报错,因为 list3 是List,调用 add() 方法是OK的,但是在运行期,会抛出 UnsupportedOperationException 异常:

Exception in thread "main" java.lang.UnsupportedOperationException
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
	at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:147)
	at org.example.Test1119_41.main(Test1119_41.java:17)

同理,使用 list3.set() 方法来改变元素的值,也会在运行期抛出异常。

原因

把Stream收集为List是一个非常常用的操作,最初,Java 8提供了 collect(Collectors.toList()) 方法,显然,因为这个操作太常用了,所以在Java 16里,将其简化为了 toList() 方法。那么问题来了:

为什么Java 8里返回的是ArrayList,而Java 16简化后,返回的是不可变List呢?

咨询了豆包,它的回答里提到好几点,比如防止意外修改,线程安全,可维护性等等,不过下面这一点我觉得最有意义:

Java 8 引入了函数式编程特性,如流(Stream)和 Lambda 表达式。在函数式编程范式中,数据的不可变性是一个重要原则。函数式编程强调无副作用的操作,不可变数据结构符合这一要求。例如,在使用流操作(如map、filter等)处理数据时,不可变列表可以保证在每一步操作中,数据的原始状态不会被改变,使得流操作的结果更加可预测和符合函数式编程的语义。

说的挺有道理的。那么问题又来了:

既然在函数式编程中,数据的不可变性很重要,很有意义,那为什么不在最初Java 8的时候, collect(Collectors.toList()) 就返回不可变List呢?

答案是:Java语言一直在演进。

在Java 8的时候,对于数据不可变性的强调还没有像 Java 9 及以后那样深入。随着对函数式编程理念的深入理解,以及在实际应用中对数据安全、代码质量等方面的更高要求,Java 设计团队逐渐认识到不可变数据结构的重要性,从而在 Java 9 及后续版本中开始大力推广和完善不可变列表等相关特性。

当然,考虑到兼容性,Java高版本不可能把 collect(Collectors.toList()) 方法的返回值修改为不可变List。

话说回来,可变List也是必要的需求。即使不可变List是主流,总会有需求要对List做修改的。

其它

Arrays.asList() 和 List.of()

本文开头有如下代码:

        var list1 = List.of("aaa", "bbbbbb", "cccc", "d", "eeeee");

这里, List.of() 方法是Java 9引入的,返回的是一个不可变List

相应的,从Java 1.2就引入的 Arrays.asList() 方法:

        var list2 = Arrays.asList("aaa", "bbb", "ggg", "ddd", "eee", "fff");

它返回的是一个受限的可变List:不能改变List的长度,只能改变元素的值:

        list2.set(3, "hhh"); // OK
        list2.add("iii"); // UnsupportedOperationException

反序(注意不是排序中的逆序)

给定一个List或Stream,如何获取反序的List或Stream(比如把 "a", "c", "b" 变成 "b", "c", "a" )?

好像没有什么特别简单的办法,一个办法是利用 Collections.reverse() 方法,比如:

        Collections.reverse(list2);

这时问题就来了,对于不可变List,没法直接reverse,只能:

  1. 先克隆成可变List
  2. 再反序
  3. 最后再克隆成不可变List(如果需要的话)
        var list3 = new ArrayList<>(list1);
        Collections.reverse(list3);
        var list4 = List.of(list3);

对于Stream,更是没办法,只能先收集成可变List,再反序(或者在收集时,使用一些手段来人工处理,更麻烦)。

为什么Stream没有提供一个反序的方法呢?这可能也是因为函数式编程的理念吧:专注于对流的数据处理,而不是改变顺序(会误认为新数据是从对应位置的原始数据变化而来的)。

总结

collect(Collectors.toList())toList()
返回值一般是ArrayList不可变List
JDK版本816
适用场景后续需要修改数据典型的流式处理
是否推荐NY

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/920050.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

wsl虚拟机中的dockers容器访问不了物理主机

1 首先保证wsl虚拟机能够访问宿主机IP地址&#xff0c;wsl虚拟机通过vEthernet (WSL)的地址访问&#xff0c;着意味着容器也要通过此IP地址访问物理主机。 2 遇到的问题&#xff1a;wsl虚拟机中安装了docker&#xff0c;用在用到docker容器内的开发环境&#xff0c;但是虚拟机…

华为VPN技术

1.启动设备 2.配置IP地址 [FW1]int g1/0/0 [FW1-GigabitEthernet1/0/0]ip add 192.168.1.254 24 [FW1-GigabitEthernet1/0/0]int g1/0/1 [FW1-GigabitEthernet1/0/1]ip add 100.1.1.1 24 [FW1-GigabitEthernet1/0/1]service-manage ping permit [FW2]int g1/0/0 [FW2-Gi…

【Swift】运算符

文章目录 术语赋值运算符算数运算符基本四则算术运算符求余运算符一元负号运算符一元正号运算符 比较运算符三元运算符空合运算符区间运算符闭区间运算符半开区间运算符单侧区间运算符 逻辑运算符逻辑非运算符逻辑与运算符逻辑或运算符逻辑运算符组合计算 位运算符运算符优先级…

二手手机回收小程序,一键便捷高效回收

随着科技的不断升级&#xff0c;智能手机也在快速进行更新换代&#xff0c;出现了大量的闲置手机&#xff0c;这为二手手机市场提供了巨大的发展空间&#xff01; 经过手机回收市场的快速发展&#xff0c;二手手机回收已经成为了消费者的新选择&#xff0c;既能够减少手机的浪…

网安瞭望台第2期:零日漏洞密集爆发、2024年常见网络安全漏洞类型及分析

国内外要闻 Ubuntu 服务器 Needrestart 软件包惊现严重安全漏洞 近日&#xff0c;Ubuntu 服务器&#xff08;自 21.04 版本起默认安装&#xff09;的 Needrestart 软件包被曝存在多个可追溯至数十年前的安全漏洞。这些漏洞允许本地攻击者在无需用户交互的情况下获取根…

反转链表、链表内指定区间反转

反转链表 给定一个单链表的头结点pHead&#xff08;该头节点是有值的&#xff0c;比如在下图&#xff0c;它的val是1&#xff09;&#xff0c;长度为n&#xff0c;反转该链表后&#xff0c;返回新链表的表头。 如当输入链表{1,2,3}时&#xff0c;经反转后&#xff0c;原链表变…

AWTK 最新动态:支持鸿蒙系统(HarmonyOS Next)

HarmonyOS是全球第三大移动操作系统&#xff0c;有巨大的市场潜力&#xff0c;在国产替代的背景下&#xff0c;机会多多&#xff0c;AWTK支持HarmonyOS&#xff0c;让AWTK开发者也能享受HarmonyOS生态的红利。 AWTK全称为Toolkit AnyWhere&#xff0c;是ZLG倾心打造的一套基于C…

CSS+JQuery 实现弹力球效果,碰到屏幕边框弹回

实现弹力球效果&#xff0c;碰到屏幕边框弹回&#xff0c;效果如下 代码如下&#xff1a; <img src"../image/ball.png" alt"" class"ball"> <style>.ball {position: fixed;top: 50vh;left: 50vw;width: 15vw;height: 15vw;border…

银河麒麟V10-SP1-x86_64离线安装Docker

由于要推广信创&#xff0c;需要把Milvus向量数据库从别的平台迁移到信创平台上&#xff0c;为了能顺利迁移&#xff0c;在迁移前需要做一系列用到的功能软件的安装与运行的测试&#xff0c;由于Milvus向量数据库依赖于Docker运行&#xff0c;以及工作性质的要求&#xff0c;只…

vue2 webpack分包实现首屏加载优化

项目打包后得到的vendor.js文件过大&#xff0c;进行拆包以减少文件的大小&#xff0c;具体实现如下&#xff1a; webpack3.x使用new webpack.optimize.CommonsChunkPlugin打包文件分割优化加载 修改项目build内的webpack.prod.conf.js文件&#xff0c;将项目中的需要拆的文件…

125.验证回文串-力扣(LeetCode)

题目&#xff1a; 解题思路&#xff1a; 首先进行移除非字母数字字符&#xff0c;并将大写字符转换为小写字符的操作。这个过程中&#xff0c;主要利用快慢指针的方式来进行移除操作&#xff0c;通过加32将大写字符转换为小写字符。完成后&#xff0c;将前一半的数据与后一半的…

ftrack 24.10全面升级:Autodesk Flame集成与多项新功能性能改进将发布

管理复杂项目绝非易事&#xff0c;但ftrack Studio的最新更新旨在简化这一过程。我们设计了这些增强功能&#xff0c;以优化大家的工作流、提高可用性&#xff0c;并让你们有更多时间专注于创意工作。 让我们来看看都有什么新内容吧&#xff01; ​增强功能来优化工作流 轻松…

深度学习基础—Bleu得分

引言 机器翻译任务中&#xff0c;通常会需要评价指标来评估机器翻译的好坏。仅通过统计翻译词在标准翻译中出现的次数这种方式很不合理&#xff0c;就需要用到Bleu得分来进行评估。 1.n-gram&#xff08;N元组&#xff09; 假设要翻译&#xff1a;Le chat est sur le tapis&am…

【MySQL】InnoDB 基本了解+存储结构

目录​​​​​​​ InnoDB简单了解 InnoDB的特性 InnoDB架构 InnoDB存储引擎创建表的数据文件 MySQL存储结构 表空间文件 用户数据在表空间中存储方式 使用页数据存储单元的原因 数据页 区 表中数据少时如果避免空间浪费 区组 段 页 数据行的组成 快速定位数据…

鸿蒙中服务卡片数据的获取和渲染

1. 2.在卡片中使用LocalStorageProp接受传递的数据 LocalStorageProp("configNewsHead") configNewsHeadLocal: ConfigNewsHeadInfoItem[] [] 注意&#xff1a;LocalStorageProp括号中的为第一步图片2中的键 3.第一次在服务卡片的第一个卡片中可能会获取不到数据…

《Django 5 By Example》阅读笔记:p211-p236

《Django 5 By Example》学习第7天&#xff0c;p211-p236总结&#xff0c;总计26页。 一、技术总结 1.messages(消息推送) django.contrib.messages。 2.OAuth 2 Django里使用的是social-app-django这个package进行认证操作。 3.开发环境使用HTTPS 使用django-extension…

机器学习(贝叶斯算法,决策树)

朴素贝叶斯分类 贝叶斯分类理论 假设现有两个数据集&#xff0c;分为两类 我们现在用p1(x,y)表示数据点(x,y)属于类别1(图中红色圆点表示的类别)的概率&#xff0c;用p2(x,y)表示数据点(x,y)属于类别2(图中蓝色三角形表示的类别)的概率&#xff0c;那么对于一个新数据点(x,y)…

《设计模式》创建型模式总结

目录 创建型模式概述 Factory Method: 唯一的类创建型模式 Abstract Factory Builder模式 Prototype模式 Singleton模式 最近在参与一个量化交易系统的项目&#xff0c;里面涉及到用java来重构部分vnpy的开源框架&#xff0c;因为是框架的搭建&#xff0c;所以会涉及到像…

【Bug合集】——Java大小写引起传参失败,获取值为null的解决方案

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;本文面向的人群 二&#xff1a;错误场景引入 三&#xff1a;正确场景引入 四&#xf…

论文阅读--supervised learning with quantum enhanced feature spaces

简略摘要 量子算法实现计算加速的核心要素是通过可控纠缠和干涉利用指数级大的量子态空间。本文在超导处理器上提出并实验实现了两种量子算法。这两种方法的一个关键组成部分是使用量子态空间作为特征空间。只有在量子计算机上才能有效访问的量子增强特征空间的使用为量子优势提…