大白话之Java Stream流:将类数组流化,便捷批量修改,通俗讲解!

前言

Stream大意概括 将一个列表(List)中的所有类铺平转换成一条流水线,按要求将流水线上的所有类一个一个地处理。像极了SQL语句

本篇文章将用通俗易懂的语言为你讲解流操作。如果你有一部分卡住看不懂,跳过去就好,再回去看就会了。

-📚-需要先了解的知识-📚-
语言内容链接
JavaList、ArrayList的使用、基本原理及区别null
JavaLambda表达式的使用及基本原理null
-⭐-评分-⭐-
知识等级实用性罕见性
进阶实用较少见

原理图

其实整个过程跟流水线差不多。让我们看看由灵魂画手A先生绘制的原理图:

屏幕快照 2019-09-11 上午10.28.23.png

没看懂没关系,做完第一个实践再回来看看。

实践

跳过那些复杂的理论,打开你的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);

将返回的新结果复用,这样就不会报错了。

如转载请在文章尾部添加

原作者来自 adlered 个人技术博客:https://www.stackoverflow.wiki/

评论

  1. 有点意思

  2. from mikolaje.github.io

  3. #1583909355439

    not bad, take a mark

取消