java8 Stream流个人笔记

粗暴地从oneNote和demo上面总结一下java8的stream流。
在学习Stream时参考了这篇博客:

https://zhuanlan.zhihu.com/p/71134330
https://zhuanlan.zhihu.com/p/33313312
https://www.jianshu.com/p/b464487ff844

  1. 什么是Stream?
    最开始接触stream的时候,以为和文件的I/O有关,查阅了解后发现这个东和I/O流只是名字差不多。stream是一个计算工具,它不能保存数据,用过的流也不能再次使用。总之,stream作为一个计算工具十分的强大,内置的api可以做各种运算操作,用了stream再看看自己以前的for和foreach(~)

  2. 构建流
    构建Stream有几种方式:
    builder
    generator
    iterate
    其中后两者需要limit来限制数量,否则会永无止尽地走下去。iterator可以看做是个符合函数,传入的第一个参数为seed,后续的输入都是前一个的输出。

// - builder
        List<String> str = Stream.<String>builder().add("a").add("b").add("c").build().collect(Collectors.toList());
        System.out.println(str);

        // - generator
        Stream<String> strGenerator = Stream.generate(() -> "俺是供给者").limit(10);
        System.out.println(strGenerator.collect(Collectors.toList()));

        // - iterate
        Stream<Integer> strIterate = Stream.iterate(40, n -> n + 40).limit(40);
        System.out.println(strIterate.collect(Collectors.toList()));

        // - Primitives
        IntStream intStream = IntStream.rangeClosed(1, 3);
        System.out.println(intStream.findFirst());

以上程序的输出结果为:

[a, b, c]
[俺是供给者, 俺是供给者, 俺是供给者, 俺是供给者, 俺是供给者, 俺是供给者, 俺是供给者, 俺是供给者, 俺是供给者, 俺是供给者]
[40, 80, 120, 160, 200, 240, 280, 320, 360, 400, 440, 480, 520, 560, 600, 640, 680, 720, 760, 800, 840, 880, 920, 960, 1000, 1040, 1080, 1120, 1160, 1200, 1240, 1280, 1320, 1360, 1400, 1440, 1480, 1520, 1560, 1600]
OptionalInt[1]
  1. Pipeline
    重点来了,Stream Pipeline能够执行源数据并提供各种聚合操作!流管道的执行顺序如下:
    源(source) -> 中间操作(Intermediate operations) -> 终结操作(terminal operation)
    操作Stream,顺序很重要,因为在只有在终结操作执行之前,前面的中间操作才会触发,中间操作都是懒触发。也就是说,他不会走多次循环,但是前面如果能过滤掉一些数据,那就应该把过滤放在前面,这样可以减少后面的中间操作的触发量(形容有点差),后面放代码来理解吧。

中间操作
Stream Api中中间操作,个人总结的常用到的有以下几种:
filter(Predicate)
map(Function)
peek(Consumer)
sorted(Comparator)
各自穿参对应了不同的函数式接口,在使用的时候留意重写对应的实现就好了。可以参考函数式接口那片博客。下面是关于中间操作的懒式调用的一个段代码:

List<String> strLazy = Arrays.asList("abc1", "abc2", "abc3");
        List<String> testLazy = strLazy.stream()
                .filter(it -> {
                    log.println("filter() is called");
                    return it.contains("2");
                })
                .map(it -> {
                    log.println("map() is called");
                    return it.toUpperCase();
                })
                .collect(Collectors.toList());
        separatorLine("2");

        Optional<String> testLazy2 = strLazy.stream().filter(element -> {
            log.println("filter() was called!");
            return element.contains("2");
        }).map(element -> {
            log.println("map() was called!");
            return element.toUpperCase();
        }).findFirst();

这段代码可以用来理解Stream中间操作的懒触发机制,这段代码的输出为:

filter() is called
filter() is called
map() is called
filter() is called
----------分割线2----------
filter() was called!
filter() was called!
map() was called!

可以看到,map is called的打印次数比filter少,但是如果换一个位置,map则和fiter的打印次数是一样的。

后续补充
看了java实战这本书,感觉好屌,第一次感受到看书比看博客有用,还有一些其他重要操作,这里就不细说了:
切片
takewhile
dropwhile
扁平流
flagMap

终结操作
stream的终结操作也可以称为流的聚合,走到这里之后就不会再有其他操作了。有两大类: reduce 和 collect
reduce(identity, accumulator, combiner)
identity是标识器,作为accumulator的初始值;
accumulator是类机器,提供聚合元素逻辑的功能,每次进行reducing都会创建一个新值,并且只有上一个值是可用的;
combiner是组合起,提供聚合accumulator的功能,能从不同线程聚合累计器的结果;
三个参数中,accumulator是必须要有的。

separatorLine("终结操作-reduce");
        List<Integer> integerList = Arrays.asList(1, 2, 3);
        System.out.println(integerList.stream().reduce((a, b) -> a + b));
        System.out.println(integerList.stream().reduce(10, (a, b) -> a + b));
        //test for reduce(combiner)
        System.out.println(integerList.stream().reduce(10, (a, b) -> a + b, (a, b) -> a + b));
        //12 - 13 = -1, 11 - (-1) = 12, and the result is 12.
        System.out.println(integerList.parallelStream().reduce(10, (a, b) -> a + b, (a, b) -> a - b));

代码输出的结果为:

Optional[6]
16
16
12

这个12很有意思,reduce支持并发的,实际结果执行过程是:12-13 = -1, 11 - (-1) = 12。如果不用并发流,第三行和第二行结果是一样的。

collect
故名意思,就是收集器。实际使用中我们常用收集器来将stream流处理后的结果根据收集器来转换为自己想要得到的数据类型。
有关收集器,有以下几个常用的方法:
toMap
toList
groupingBy
joinning(delimiter, prefix, suffix)
toMap是根据key value来生成一个键值对;toList顾名思义就是生成一个List;groupingBy和SQL中的groupBy作用一样,指定一个key进行聚合操作;joining则是拼接成字符串,传参为分隔符,前缀和后缀。

separatorLine("终结操作-collect");
        List<Product> products = Arrays.asList(new Product(1, "南瓜", 15)
                , new Product(2, "黄瓜", 8)
                , new Product(3, "冬瓜", 12)
                , new Product(4, "苦瓜", 16));
        Map<Integer, Product> productMap = products.stream().collect(Collectors.toMap(Product::getId, identity()));
        Map<Integer, List<Product>> productMap2 = products.stream().collect(Collectors.groupingBy(Product::getId));
        String listToStringTest = products.stream().map(Product::getName).collect(Collectors.joining(",","[","]"));
        System.out.println(listToStringTest);

这里没有做map的遍历,以上代码的输出结果为:

[南瓜,黄瓜,冬瓜,苦瓜]

Stream是java8的核心之一,需要操作数据时要有意识去用这个,增强代码的可读性,也在部分场景让代码的复用也变得清晰明了。个人对于这一块的总结暂时就到此。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值