前言
Stream大意概括
将一个列表(List)中的所有类铺平转换成一条流水线,按要求将流水线上的所有类一个一个地处理。像极了 SQL 语句。
本篇文章将用通俗易懂的语言为你讲解流操作。如果你有一部分卡住看不懂,跳过去就好,再回去看就会了。
-📚-| 需要先了解的知识 |-📚-
语言 | 内容 | 链接 |
---|---|---|
Java | List、ArrayList 的使用、基本原理及区别 | null |
Java | Lambda 表达式的使用及基本原理 | null |
-⭐️-| 评分 |-⭐️-
知识等级 | 实用性 | 罕见性 |
---|---|---|
进阶 | 实用 | 较少见 |
原理图
其实整个过程跟流水线差不多。让我们看看由灵魂画手 A 先生绘制的原理图:
没看懂没关系,做完第一个实践再回来看看。
实践
跳过那些复杂的理论,打开你的 IDE,新建一个类名为 Arr,把下面的代码复制进去,反复分析代码,最后带着你的疑问再继续:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Arr {
public static void main(String[] args) {
//God创建星球
星球 星球1 = new 星球();
星球 星球2 = new 星球();
星球 星球3 = new 星球();
//God给星球起名
星球1.设置星球名("地球");
星球2.设置星球名("太阳");
星球3.设置星球名("唱跳Rap篮球");
//God把星球添加到ArrayList中
List<星球> 宇宙 = new ArrayList();
宇宙.add(星球1);
宇宙.add(星球2);
宇宙.add(星球3);
//灭霸出现
Stream<星球> 灭霸 = 宇宙.stream().map(
星球 -> {
//如果星球名中带“球”字,则毁灭
if (星球.获取星球名().indexOf("球") != -1) {
星球.设置星球名("已毁灭");
}
return 星球;
});
//将全部星球重新转换为新宇宙
List<星球> 新宇宙 = 灭霸.collect(Collectors.toList());
//打印星球存活情况
for (星球 i : 新宇宙) {
System.out.println(i.获取星球名());
}
}
}
class 星球 {
private String 星球名;
public void 设置星球名(String 星球名) {
this.星球名 = 星球名;
}
public String 获取星球名() {
return 星球名;
}
}
map()
就是将流中的类挨个儿揪出来,并临时命名为一个局部的星球
变量,并且对这个变量进行判断呢修改后,再返回新的星球
给灭霸。
运行结果:
已毁灭
太阳
已毁灭
整理思路
⬇️ 反复运行并分析结果,然后和我一起整理下思路 ⬇️ | |
---|---|
1 | 星球 是一个类,存储了 星球名 ,并提供了设置与获取星球名的方法; |
2 | 主方法 中,我们实例化了 3 个星球,并将其命名为“地球”、“太阳”、“唱跳 Rap 篮球”; |
3 | 宇宙 是一个列表,而后我们将 3 个星球添加到了 宇宙 中; |
4 | 灭霸 是实例化的流操作,执行 宇宙.stream() 后就能得到我们的流; |
5 | 使用流进行了 map() 操作后,使用 灭霸.collect() 方法将 灭霸 中的流重新收集为一个列表,名为 新宇宙 ; |
6 | 遍历 新宇宙 后,我们发现数据已经修改成功。 |
其中的流化操作,就像是下边这条伪 SQL 语句:
UPDATE 宇宙 SET 星球名=IF(... .indexOf("球") != -1 ...);
SQL 语义为:将所有宇宙中的星球名更新,其中如果包含“球”的则修改为“已毁灭”,其它不变。
那么如果下面这条 SQL 语句,应该怎么表达?
SELECT * FROM 宇宙 WHERE LOCATE("球", 星球名) LIMIT 1;
将星球名包含“球”字的星球筛选(即不打印不包含“球”字的星球名),并限制仅打印一个。
修改灭霸部分:
Stream<星球> 灭霸 = 宇宙.stream()
.filter(星球 -> 星球.获取星球名().indexOf("球") != -1)
.limit(1);
运行结果:
地球
其它流操作
其它的几个流操作:
去重 stream().distinct()
排序 stream().sorted()
扔掉前 N 个元素 stream().skip(n)
自己尝试下吧!
Stream.of
我们演示使用的 List
中的 .stream()
方法是继承自 Collection
集合类的,所以也就是说,只要是基于 Collection
的集合,大多数都是支持使用 Stream
类进行流操作的。
那么如果我想省略掉建立 List 步骤,直接生成为一个 Stream
流呢?
Stream.of(星球1, 星球2, 星球3);
将代码套用到刚刚的实例中。修改主方法:
public static void main(String[] args) {
//God创建星球
星球 星球1 = new 星球();
星球 星球2 = new 星球();
星球 星球3 = new 星球();
//God给星球起名
星球1.设置星球名("地球");
星球2.设置星球名("太阳");
星球3.设置星球名("唱跳Rap篮球");
//灭霸出现
Stream<星球> 灭霸 = Stream.of(星球1, 星球2, 星球3)
.filter(星球 -> 星球.获取星球名().indexOf("球") != -1)
.limit(1);
//将全部星球重新转换为新宇宙
List<星球> 新宇宙 = 灭霸.collect(Collectors.toList());
//打印星球存活情况
for (星球 i : 新宇宙) {
System.out.println(i.获取星球名());
}
}
是不是优美了一点儿呢?
后语
排坑
如果你将灭霸部分修改为:
Stream<星球> 灭霸 = Stream.of(星球1, 星球2, 星球3);
灭霸.filter(星球 -> 星球.获取星球名().indexOf("球") != -1)
.limit(1);
再次运行,你会发现出现报错:
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at Arr.main(Arr.java:21)
这是由于每个 Stream 只能使用一次。将代码稍稍修改:
Stream<星球> 灭霸 = Stream.of(星球1, 星球2, 星球3);
灭霸 = 灭霸.filter(星球 -> 星球.获取星球名().indexOf("球") != -1)
.limit(1);
将返回的新结果复用,这样就不会报错了。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于