0202年了,还没有用上Java函数式编程!!!——Stream流


Stream.png

什么是Stream流

概念

在Stream类,有这么一句话:

A sequence of elements supporting sequential and parallel aggregate operations.

简单翻译过来就是支持顺序和并行汇集的一系列元素,咋一看,跟集合有点类似,对,在Java8的API中,它允许你用函数式的方式对集合进行操作,是集合运算和表达的高阶抽象。

构建

通过Collection接口

Collection接口,自带有stream默认方法,如某普通的ArrayList对象,可以直接调用该方法构建stream流流:

 		//new一个普通list对象
        List<Integer> list = new ArrayList<Integer>() {{ add(1);add(2);add(3); }};
        //调用默认方法stream转化成Stream流
        list.stream();

通过Stream.of方法

用Stream类的静态方法of构建Stream流

        //Stream.of用元素直接构建流
        Stream.of (1,2,3);

操作

在基本操作之前,我们得了解两个基本概念:

  • 中间操作(Intermediate Operations:中间操作会返回一个新的Stream流,惰性操作,未真正遍历)
  • 最终操作(Terminal Operations:是指返回最终的结果。一个流只能有一个最终操作,会真正开始流的遍历,并且会生成一个结果)

用构建章节的例子说明:

      list.stream().filter(num -> num > 1).count();

stream(): 构建一个Stream流。
filter(num -> num > 1): 中间操作:过滤集合里大于1的数,惰性,未执行。
count(): 最终操作:计算大于1的数的个数,开始遍历执行。

列举下Stream的操作分类:

Stream操作分类
中间操作(Intermediate Operations) 无状态(Stateless) unordered() filter() map() mapToInt() mapToLong()mapToDouble() flatMap() flatMapToInt() flatMapToLong()flatMapToDouble() peek()
有状态(Stateful) distinct() sorted() limit() skip()
最终操作(Terminal Operations) 非短路操作 forEach() forEachOrdered() toArray() reduce() collect() max() min() count()
短路操作(short-circuiting) anyMatch() allMatch() noneMatch() findFirst() findAny()

解释一下上表的中几个其他概念:

中间操作的无状态

即某个Stream流调用无状态的中间操作方法时,可以独立的对Stream流里的某一个元素进行操作,此操作不会受到其他元素的影响。如map()操作,对流里的每个元素进行映射操作。

中间操作的有状态

即某个Stream流调用有状态的中间操作方法时,要拿到Stream流的所有元素才能操作。
如distinct() 操作,得拿到所有元素,才能进行去重操作,而不是拿某一个元素,就开始处理。

最终操作的短路操作

即找到符合条件的元素后就能得到最终结果。如findFirst(),找到符合条件的第一个,就结束操作。

最终操作的非短路操作

即处理所有元素后才能得到最终结果。如forEach() ,循环处理所有元素。

Stream常见方法

filter()

过滤操作,操作章节,已经展示,不重复了。

collect()

收集操作,用于将Stream流的元素收集起来,转化成一个list,用上面的例子,写法如

list.stream().collect(Collectors.toList())

map()

map操作可以将Stream流里的元素,从一种类型转换成另一种类型,即一种流转换成另一个流。下面例子简单展示了,map()将Stream流里的每个Integer元素转化成String类型。

list.stream()
    .map((num)->Integer.toString(num))
    .collect(Collectors.toList())

flatMap()

flatMap 扁平化的map转化,为了理解,我们设计一个需求,分割字符串“Hello World”成单个字母,并且去重后,转化成list,输出。
我们很容易会想到用map这样的操作,如下:

Stream.of("Hello World")
      .map(str->str.split(""))
      .distinct()
      .collect(Collectors.toList());

输出(省去了输出打印的代码):

[H, e, l, l, o,  , W, o, r, l, d]

但这样不行,Stream.of(“Hello World”)是构建了一个元素的Stream流, 而map操作恰恰是针对单个元素进行处理,所以仅仅是把单个"Hello World"元素转化成单个String[]类型元素, 像distinct操作要需要多个元素的才能处理,所以做如下修改:

Stream.of("Hello World")
      .map(str->str.split(""))
      .flatMap(Stream::of)
      .distinct()
      .collect(Collectors.toList());

用flatMap操作,把上面map操作转化的String[]类型元素构建成多个Stream流(每个String[]里的String都构建成一个流),使distinct操作起作用。
输出:

[H, e, l, o,  , W, r, d]

reduce()

reduce 操作可以实现从一组值中生成一个值,比如说实现一个累加操作。

   Stream.of(1, 2, 3) .reduce(0, (acc, element) -> acc + element);

将两个参数相加,acc 是累加器,保存着当前的 累加结果。
输出:6

parallel()

获取Stream并行流,比如说多个任务,利用多个和多核cpu并行执行(原理是fork/join框架),提高运行的效率,但不代表一定比串行快,受数据大小、集合数据结构、值是否装箱、cpu核数、处理每个元素的时间的影响,所以举个比较夸张的例子,让parallel体现出效果。

IntStream.range(0, 1000000000)
                .parallel()
                .summaryStatistics()
                .getSum()

构建0-1000000000的Stream流,parallel操作获取并行Stream后,进行统计求和。这时候执行效率如下:
串行情况(即不使用parallel操作)

499999999500000000
运行时间为:1382ms

并行情况(使用parallel操作)

499999999500000000
运行时间为:393ms

Java8 其他特性

  • 接口的默认方法和接口的静态方法。
  • Optional:是为核心类库新设计的一个数据类型,用来替换 null 值,和提供各种 null值处理操作 。
  • CompletableFuture 异步编程

总结

  1. 了解Stream流的构建方式(调用stream默认方法,Stream.of静态方法)。
  2. 了解Stream流的操作类型(中间操作,最终操作)。
  3. 了解Stream流的操作方法(常见如:filter、map、flatMap、reduce、parallel)

最后

以上的例子,较为简单,主要是考虑如何更快理解,实际使用场景,还需是结合实际,多查阅Java8 Stream Api,做到举一反三。
欢迎大家留言讨论,同时欢迎转载,但请注明出处和作者。
所有示例,已经上传至我的github https://github.com/zhiwen-li/Exemplar-learning

参考

daf145d7ef4c5fc94e59fadcafe074c8.png

发布了2 篇原创文章 · 获赞 5 · 访问量 364

猜你喜欢

转载自blog.csdn.net/u011664145/article/details/105662065