美文网首页java
Java8中的Stream

Java8中的Stream

作者: HachiLin | 来源:发表于2020-09-09 15:04 被阅读0次

1. 什么是Stream

  Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。

  Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。

  而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。

2. 为什么需要Stream

  Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。所以说,Java 8 中首次出现的 java.util.stream 是一个函数式语言+多核时代综合影响的产物。

3. Stream两种操作

  • Intermediate(中间操作) :一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。其操作包括:

filter()、map()、flatMap()、distinct()、sorted()、peek()、limit()、skip()

  • Terminal (终端操作):一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。其操作包括:

forEach()、forEachOrdered()、toArray()、reduce()、collect()、min()、max()、count()、anyMatch()、allMatch()、noneMatch()、findFirst()、findAny()

4. 流的构造与转换

4.1 构造流的几种常见方法

// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String[] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();

4.2 数值流的构造

一个 Stream 只可以使用一次,下面的代码为了简洁而重复使用了数次。

IntStream.of(new int[]{1, 2, 3}).forEach(System.out::println);
IntStream.range(1, 3).forEach(System.out::println);
IntStream.rangeClosed(1, 3).forEach(n -> System.out.print(n+ " "));

forEach 不能修改自己包含的本地变量值,也不能用 break/return 之类的关键字提前结束循环。

4.3 流转换为其它数据结构

// 1. Array
String[] strArray1 = stream.toArray(String[]::new);
// 2. Collection
List<String> list1 = stream.collect(Collectors.toList());
List<String> list2 = stream.collect(Collectors.toCollection(ArrayList::new));
Set set1 = stream.collect(Collectors.toSet());
Stack stack1 = stream.collect(Collectors.toCollection(Stack::new));
// 3. String
String str = stream.collect(Collectors.joining()).toString();

5. 常用的Straem操作

5.1 filter():筛选流中满足条件的数据

// 返回大于2的元素集合
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> result = nums.stream().filter(n -> n > 2).collect(Collectors.toList());
System.out.println(result);

返回结果:[3,4,5]

5.2 map():将流中的数据进行一对一映射

  • 例1
// 数组中所有元素,按某种规律进行计算
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> result = nums.stream().map(n -> n**2).collect(Collectors.toList());
System.out.println(result);

返回结果:[1,4,9,16,25]

  • 例2
// 数组中字母小写变大写
List<String> nums = Arrays.asList("a", "b", "c", "d");
List<Integer> result = nums.stream().map(String::toUpperCase).collect(Collectors.toList());
System.out.println(result);

返回结果:["A", "B", "C", "D"]

5.3 flatMap():扁平化映射,将多个stream连接成一个stream

// 将多层集合中的元素取出来,放到一个新的集合中去
List<Integer> num1 = Arrays.asList(1, 2, 3);
List<Integer> num2 = Arrays.asList(4, 5, 6);
List<Integer> num3 = Arrays.asList(7, 8, 9);
List<List<Integer>> lists = Arrays.asList(num1, num2, num3);
Stream<Integer> outputStream = lists.stream().flatMap(childList -> childList.stream());
List<Integer> flatMapResult = outputStream.sorted().collect(Collectors.toList());
System.out.println(flatMapResult);

返回结果:[1, 2, 3, 4, 5, 6, 7, 8, 9]

5.4 去除stream中的重复元素

LIst<String> list = Arrays.asList("a","b","c","d","a","b");
Stream<String> stream = list.stream();
List<String> mzc = stream.distinct().collect(Collectors.toList());
System.out.println(mzc);

返回结果:["a","b","c","d"]

5.5 sorted():将流中元素进行排序

List<Integer> nums = Arrays.asList(1, 3, 5, 6, 8, 2);
// 升序排序
List<Integer> sortedNum = nums.stream().sorted().collect(Collectors.toList());
System.out.println(sortedNum);
 
// 降序排序
List<Integer> sortedNum2 = nums.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
System.out.println(sortedNum2);
 
// 使用Comparator排序
List<Integer> sortedNums3 = nums.stream().sorted(Comparator.comparing(n -> n)).collect(Collectors.toList());
System.out.println(sortedNums3);
 
// 不用stream直接顺序排序
nums.sort(Comparator.comparing(Integer::intValue));
System.out.println(nums);
 
//不用stream直接降序排序
nums.sort(Comparator.comparing(Integer::intValue).reversed());
System.out.println(nums);

返回结果:
[1, 2, 3, 5, 6, 8]
[8, 6, 5, 3, 2, 1]
[1, 2, 3, 5, 6, 8]
[1, 2, 3, 5, 6, 8]
[8, 6, 5, 3, 2, 1]

5.6 peek():对每个元素执行操作并返回一个新的Stream

Stream.of("one", "two", "three", "four")
 .filter(e -> e.length() > 3)
 .peek(e -> System.out.println("Filtered value: " + e))
 .map(String::toUpperCase)
 .peek(e -> System.out.println("Mapped value: " + e))
 .collect(Collectors.toList());

返回结果:
Filtered value: three
Mapped value: THREE
Filtered value: four
Mapped value: FOUR

5.7 limit/skip

limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素(它是由一个叫 subStream 的方法改名而来)。

List<Integer> list = Stream.iterate(1, item -> item+1).limit(10).skip(3).collect(Collectors.toList());
System.out.println(list);

返回结果:[4,5,6,7,8,9,10]

6. 总结

总之,Stream 的特性可以归纳为:

  • 不是数据结构
  • 它没有内部存储,它只是用操作管道从 source(数据结构、数组、generator function、IO channel)抓取数据。
  • 它也绝不修改自己所封装的底层数据结构的数据。例如 Stream 的 filter 操作会产生一个不包含被过滤元素的新 Stream,而不是从 source 删除那些元素。
  • 所有 Stream 的操作必须以 lambda 表达式为参数
  • 不支持索引访问
  • 你可以请求第一个元素,但无法请求第二个,第三个,或最后一个
  • 很容易生成数组或者 List
  • 惰性化
  • 很多 Stream 操作是向后延迟的,一直到它弄清楚了最后需要多少数据才会开始。
  • Intermediate 操作永远是惰性化的。
  • 并行能力
  • 当一个 Stream 是并行化的,就不需要再写多线程代码,所有对它的操作会自动并行进行的。
  • 可以是无限的
    集合有固定大小,Stream 则不必。limit(n) 和 findFirst() 这类的 short-circuiting 操作可以对无限的 Stream 进行运算并很快完成。

参考文献

相关文章

  • JAVA8中Stream学习

    JAVA8中Stream学习 最近看了下Stream的api: Stream用户操作Collection族的数据,...

  • Java8之Stream流(六)收集

    Java8之Stream流(一)基础体验 Java8之Stream流(二)关键知识点 Java8之Stream...

  • Java8 Stream 流的重用

    Java8 Stream 已经被操作或关闭 引言 在 java8 中,Stream 不能被重用,一旦它被使用或使用...

  • Stream

    使用 Stream API 高逼格 优化 Java 代码 Java8 Stream流中的 collect() 别再...

  • Java 8 Stream--开发手册

    什么是Java8 Stream,为什么需要Stream? Stream是Java8一大亮点,它与 java.io ...

  • Java8新特性——强大的Stream

    Stream API 了解Stream Java8中有两个比较大的改变 Lambda表达式 Stream API ...

  • Java 8 Stream 教程

    本文采用实例驱动的方式,对JAVA8的stream API进行一个深入的介绍。虽然JAVA8中的stream AP...

  • Java8 Stream-API

    Java8 Stream-API Stream 流类似集合类中的Iterator,但是比Iterator高级,只需...

  • 怎么break java8 stream的foreach

    怎么break java8 stream的foreach 简介 我们通常需要在java stream中遍历处理里面...

  • Java8新特性

    一、Lambda案例 案例1 案例2 二、Stream接口 Stream介绍 Stream在Java8中被定义为泛...

网友评论

    本文标题:Java8中的Stream

    本文链接:https://www.haomeiwen.com/subject/xcteektx.html