可以向ToList()返回的集合Add元素吗?

开发 前端
架构如果Spring家族没有推出Spring Boot,Spring是有被取代风险的,因为那时的开发者对它的配置繁琐、使用曲线较高已有所反感(即使比EJB还轻太多)。

📚前言

读过《Java核心技术》的同学可能记得里面有一句话:“注意不要编写返回引用可变对象的访问器方法”。对于这句话,笔者在和同事交流常表示:若你遇见有同事代码能这么写的,一定值得你的高看(虽不一定有实际作用),因为这就是coding sense,相对稀有。

本文讨论的议题是:Stream流返回集合时,是否可以继续向此集合add元素?

✍正文

Java 8的Stream流有两大特点:

  • 不可变:不影响原集合,每次调用都返回一个新的Stream
  • 延迟执行:在遇到终结操作之前,Stream不会执行

这里面有个“不可变”,针对于这里它有两重含义:

  1. 每次操作都会生成一个新的Stream。因此Stream是不可变的(就像LocalDate、String等)
  2. 原集合不受影响。在进行数据操作时,不会对原来的集合元素有影响

总之,在使用Stream时,我们不用关心操作对原集合带来的“副作用”,非常省心。

toList()/toSet()返回的集合类型

Stream操作最常用的莫过于toList()和toSet()两个Collector收集方式,看看返回的是什么类型勒。

toList()

@Test
public void fun() {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);

List<Integer> streamResultForList = list.stream().collect(toList());
System.out.println("toList()返回的类型:" + streamResultForList.getClass());
System.out.println(streamResultForList.getClass() == list.getClass());
}

运行程序,输出:

toList()返回的类型:class java.util.ArrayList
true

toSet()

@Test
public void fun1() {
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);

Set<Integer> streamResultForSet = set.stream().collect(toSet());
System.out.println("toSet()返回的类型:" + streamResultForSet.getClass());
System.out.println(streamResultForSet.getClass() == set.getClass());
}

运行程序,输出:

toSet()返回的类型:class java.util.HashSet
true

结论:

  • toList()返回的是ArrayList类型
  • toSet()返回的是HashSet类型

原理

已经知道了返回类型,就顺势再走近一点,看看为啥返回的是这个结果呢?

其实仅仅只需向前一小步,点进去源码一看便知:

图片

图片

标题问题的答案

可以。不管是toList()还是toSet()返回的都是咱最常用的集合类型,所以肯定可以add元素呀。

@Test
public void fun2() {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);

System.out.println(list.getClass());

List<Integer> streamResultForList = list.stream().collect(toList());
streamResultForList.add(10);
System.out.println("stream后的集合:" + streamResultForList);
System.out.println("源集合:" + list);
}

运行程序,输出:

class java.util.ArrayList
stream后的集合:[1, 2, 3, 10]
源集合:[1, 2, 3]

stream后的集合成功添加了元素10,但源集合不受影响哦。

如何返回不可变集合

返回不可变引用(对象、集合)是提高程序健壮性的有效手段之一,那么如何做到返回一个不可变(或者线程安全)的集合呢?

其实,上面的截图里,JDK已经给了我们答案:

图片

简而言之:如果希望返回自己控制的结合类型,请使用toCollection(Supplier)收集器,具体返回什么样的集合,交给使用者实现Supplier。

这里,笔者通过三种方式用三种方式来返回不可变集合类型,供你参考:

方式一:直接提供一个不可变集合的实例

@Test
public void fun4() {
List<Integer> list = new ArrayList<>();

Collection<Integer> streamCollection = list.stream().collect(toCollection(()-> Arrays.asList()));
System.out.println("stream后的集合类型:" + streamCollection.getClass());
streamCollection.add(10);
}

运行结果:
stream后的集合类型:class java.util.Arrays$ArrayList
java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)

方式二:间接提供一个不可变集合的实例

@Test
public void fun5() {
List<Integer> list = new ArrayList<>();

Collection<Integer> streamCollection = list.stream().collect(toCollection(() -> Collections.unmodifiableList(new ArrayList<>())));
System.out.println("stream后的集合类型:" + streamCollection.getClass());
streamCollection.add(10);
}

运行结果:
stream后的集合类型:class java.util.Collections$UnmodifiableRandomAccessList
java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableCollection.add(Collections.java:1057)

方式三:使用collectingAndThen分布进行(推荐)

@Test
public void fun6() {
List<Integer> list = new ArrayList<>();

Collection<Integer> streamCollection = list.stream()
.collect(collectingAndThen(toSet(), l -> Collections.unmodifiableSet(l)));
System.out.println("stream后的集合类型:" + streamCollection.getClass());
streamCollection.add(10);
}

运行结果:
stream后的集合类型:class java.util.Collections$UnmodifiableSet
java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableCollection.add(Collections.java:1057)

给你留哥问题:返回线程安全的集合类型,你会了吗?

🍞总结

本文除了弄清楚标题所描述的问题外,另一目的是建议coder们都能有引用类型“不可变”的意识(当然后面有专文分享这个话题),代码水平或许就是这样一步步提升的,积跬步方可至千里。

本专栏源代码库:https://github.com/yourbatman/yourbatman-999-question

  • 个人博客:https://yourbatman.cn
  • 程序员网盘:https://wangpan.yourbatman.cn
  • 女娲工程:https://start.yourbatman.cn
  • 更多专栏:​​https://yourbatman.cn/columns​​ |或| 公号后台回复“专栏列表”获取全部小而美的原创技术专栏

我是YourBatman,一个俗人,贪财好色。历经过延期毕业、卖保险、送外卖的大龄程序员,《梦幻西游》骨灰玩家;龙珠迷、火影迷。现资深领域建模专家、Java架构师;高质量代码、DDD面向对象设计布道师;Spring开源贡献者,CSDN博客之星年度Top 10,出版书籍《Spring奇淫巧技》&《领域建模之面向对象程序设计》进行时。

责任编辑:武晓燕 来源: YourBatman
相关推荐

2023-08-13 16:17:31

2010-04-14 15:09:49

Oracle函数

2023-11-07 07:39:56

Java集合数据结构

2022-01-07 19:50:14

元素java集合

2023-03-04 21:05:53

JAVA泛型通配符

2009-12-21 15:33:07

WCF集合元素

2009-12-22 16:50:44

ADO.NET元素

2011-08-12 11:04:47

Oracle数据库增删集合元素Java

2009-11-19 17:04:30

动态路由技术

2020-08-06 07:49:57

List元素集合

2012-03-19 09:57:09

JavaArrayList

2019-03-04 09:22:52

阿里巴巴foreach Java

2011-05-12 18:21:42

C++

2022-12-14 09:10:06

JAVA注解继承

2023-11-17 18:01:48

CIOCTO

2018-04-02 15:19:31

比特币区块链中本聪

2019-07-24 15:33:55

大数据数据处理分析

2014-11-04 11:13:54

EdisonArduino

2023-02-27 07:56:55

IngressKubernetes

2009-12-17 13:37:24

Ruby代码块
点赞
收藏

51CTO技术栈公众号