Stream 是 Java8 的新特性,区别于 IO 的流,它是对容器对象功能的增强,为操作容器对象提供便利、高效的方法的同时,简化代码

[toc]

基本概念

使用流时,通常包括三个基本步骤:创建流 -> 中间操作 -> 终止操作。

流常用的相关接口如下,其中 IntStream LongStreamDoubleStream 对应三种基本类型(int, long, double,注意不是包装类型),Stream 对应所有剩余类型:

1
2
3
4
5
public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable
public interface IntStream extends BaseStream<Integer, IntStream>
public interface DoubleStream extends BaseStream<Double, DoubleStream>
public interface LongStream extends BaseStream<Long, LongStream>
public interface Stream<T> extends BaseStream<T, Stream<T>>

stream 的几点注意事项:

  1. 它不是一种数据结构,只是某种数据源的一个 view,因此对 stream 的任何操作都不会影响真实数据,而是会产生一个新 stream
  2. 惰式执行:stream 调用中间操作并不会立即执行,而只是生成一个标记了该操作的新 stream,只有调用终止操作会触发实际计算,计算发生时会把所有中间操作积攒的操作以管道 pipeline 的方式执行,这样可以减少迭代次数。计算完成之后 stream 就会失效。
  3. 可消费性:stream 只能被 “消费” 一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成

创建流

创建方法
集合 Collection.stream()、Collection.stream()
数组 Arrays.stream()
数值流 IntStream、LongStream、range、rangeClosed
Random.ints()、Random.longs()、Random.doubles()
自己创建 Stream.generate()、Stream.iterate()
文件 Files.lines()
Pattern splitAsStream()

Stream.of 创建

使用 Stream.of 静态方法创建

1
Stream<String> stream = Stream.of("A", "B", "C", "D");

从 Collection 创建

1
2
list.stream();
list.parallelStream(); // 并行流

从数组创建

1
2
int[] intArr = {1, 2, 3, 4, 5};
IntStream arrayStream = Arrays.stream(intArr);

使用数值流可以减少计算过程中拆箱装箱导致的大量耗时,提高性能。

Stream API 提供了 mapToInt()mapToDouble()mapToLong() 三种方式将普通 Stream 转换成对应的数值流,同时提供了中间方法 boxed() 将数值流转换为对象流

从文件创建

1
2
// 每个元素代表文件的一行内容
Stream<String> lines = Files.lines(Paths.get("/path/to/file.txt"))

从字符串创建

1
2
3
4
5
6
7
8
9
10
// 以表达式作为分隔符
String REGEX = "ee";
Pattern pattern = Pattern.compile(REGEX);
Stream<String> stream = pattern.splitAsStream("aaeebbeecceeddee");
stream.forEach(System.out::println);
// 输出:
// aa
// bb
// cc
// dd

中间(Intermediate)操作

中间操作分为:

  • 无状态(Stateless)操作:指元素的处理不受之前元素的影响
  • 有状态(Stateful)操作:指该操作只有拿到所有元素之后才能继续下去
相关方法 含义
无状态操作 map() / mapToXXX() 接受一个函数作为参数。这个函数会被应用到每个元素上,并在新流中将其映射成一个新的元素
flatMap() / flatMapToXXX() 与 map() 类似,一对多,多个流被合并成一个流
filter() 通过设置的条件过滤出元素
peek() 对元素进行遍历处理,与 forEach() 一样,但它是中间操作
有状态操作 distinct() 返回一个元素各异(根据流所生成元素的 hashCode() 和 equals() 方法实现)的流
sorted() 返回排序后的流,可传入自定义的 Comparator
limit() 返回一个不超过给定长度的流
skip() 返回一个扔掉了前 n 个元素的流

map()、filter()、distinct()

1
2
3
4
5
6
7
8
9
10
String str = "good good study and day day up";
Stream.of(str.split(" "))
.filter(s -> s.length() > 2)
.map(s -> s.length())
.distinct()
.forEach(System.out::println);
// 输出:
// 4
// 5
// 3

flatMap()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
String str = "good good study and day day up";
// intStream/longStream 并不是Stream的子类, 所以要进行装箱
Stream.of(str.split(" "))
.flatMap(s -> s.chars().boxed()) // 参数是Function,
.forEach(i -> System.out.println((char) i.intValue()));
// 输出:
// g
// o
// o
// .
// .
// .
// u
// p

sorted()

1
2
3
4
5
6
7
8
// 实现按绝对值降序排序
int[] nums = new int[]{5, 1, -1, -3, 0};
nums = IntStream.of(nums)
.boxed()
.sorted((a, b) -> Math.abs(b) - Math.abs(a))
.mapToInt(a -> Integer.valueOf(a))
.toArray();
// 此时 nums 变成 [5, -3, 1, -1, 0]

peek()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
String str = "good good study and day day up";
Stream.of(str.split(" "))
.peek(System.out::println) // 参数是个Consumer
.forEach(System.out::println);
// 输出:
// good
// good
// good
// good
// study
// study
// and
// and
// day
// day
// day
// day
// up
// up

limit()

1
2
3
4
5
6
7
8
new Random().ints().filter(i -> i > 100 && i < 1000).limit(5)
.forEach(System.out::println);
// 输出:
// 593
// 321
// 964
// 194
// 346

终止(Terminal)操作

终止操作分为:

  • 短路(Short-circuiting)操作:指遇到某些符合条件的元素就可以得到最终结果
  • 非短路(Unshort-circuiting)操作:指必须处理完所有元素才能得到最终结果
相关方法 含义
非短路操作 forEach() / forEachOrdered() 遍历流。对于并行流,forEachOrdered() 保证维持流的顺序
collect() 将流转换为其他形式
toArrary() 转化成一个 Object 数组,对于 IntStream 等基本类型流,返回对应的基本类型数组
reduce() 将流中元素反复结合起来,得到一个值
min() / max() / sum() / count() 获取最小值/最大值/和/元素个数
短路操作 findFirst() / findAny() 返回第一个/任意元素
allMatch() / anyMatch() / noneMatch() 检查所有的元素与规则的匹配情况

forEach()

forEach() 不能修改自己包含的本地变量值,也不能使用 breakreturn 等提前结束循环

1
2
3
4
5
6
7
Stream<Integer> streamNumbers = Stream.of(1, 2, 3, 4);
streamNumbers.forEach(System.out::println);
// 输出:
// 1
// 2
// 3
// 4

forEachOrdered()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.parallelStream().forEach(System.out::println);
// 输出:
// 5
// 4
// 1
// 3
// 2
// 7
// 6
list.parallelStream().forEachOrdered(System.out::println);
// 输出:
// 1
// 2
// 3
// 4
// 5
// 6
// 7

collect()

toArray()

1
2
3
4
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
Object [] strAryNoArg = list.stream().toArray();
// 提供参数时,则使用提供的 generator 函数分配返回的数组
Integer [] strAry = list.stream().toArray(Integer[]::new);

reduce()

将流中元素反复结合。可选参数 identity 作为起始值(返回具体类型)或前两个元素作为起始值(返回 Optional 类型)。字符串拼接、数值的 sum、min、max、average 等可看作特殊的 reduce()

1
2
3
4
5
6
7
8
9
10
11
12
IntStream arrayStream = Arrays.stream(new int[]{1, 2, 3, 4, 5});
int sum = arrayStream.reduce(0, ((e1, e2) -> e1 + e2));
System.out.println(sum);
// 输出:
// 15

String concat = Stream.of("a", "B", "c", "D", "e", "F").
filter(x -> x.compareTo("Z") > 0).
reduce(String::concat).get();
System.out.println(concat);
// 输出:
// ace

min()、max()、sum()、count()

min()max() 需要传入一个比较器,count() 返回的类型是 longsum()IntStream 等数值流的方法

findAny()max()min()reduce() 等方法等返回 Optional 容器,它可能含有某值,或者不包含,使用它是为了避免 NullPointerException

1
2
3
4
5
6
7
8
9
Optional.ofNullable(text).ifPresent(System.out::println);
// 等价于
if (text != null) {
System.out.println(text);
}

return Optional.ofNullable(text).map(String::length).orElse(-1);
// 等价于
return if (text != null) ? text.length() : -1;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
List<Integer> list = Arrays.asList(-1, 2, -3, 4, -5, 6, -7);
int maxAbs = list.stream().max((e1, e2) -> Math.abs(e1) - Math.abs(e2)).get();
System.out.println(maxAbs);
// 输出:
// -7

list = Arrays.asList(-1, 2, -3, 4, -5, 6, -7);
int minAbs = list.stream().max((e1, e2) -> Math.abs(e1) - Math.abs(e2)).get();
System.out.println(minAbs);
// 输出:
// -1

list = Arrays.asList(-1, 2, -3, 4, -5, 6, -7);
long counts = list.stream().count();
System.out.println(counts);
// 输出:
// 7

list = Arrays.asList(-1, 2, -3, 4, -5, 6, -7);
int sum = list.stream().mapToInt(e -> Integer.valueOf(e)).sum();
System.out.println(sum);
// 输出:
// -4

findFirst()、findAny()

1
2
3
4
5
6
7
8
9
10
11
12
13
Stream<Integer> stream = Stream.of(3, 1, 10, 16, 8, 4, 9);
// 调用 Optional 的 get() 获取返回的元素
System.out.println(stream.findFirst().get());
// 输出:
// 3

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
int result = list.parallelStream().filter(e -> e > 5).findAny().get();
System.out.println("result = " + result);
// 输出:
// 6
// 或者
// 7

anyMatch() 、allMatch()、noneMatch()

1
2
3
4
5
6
7
8
9
10
11
12
Stream<Integer> stream = Stream.of(3, 1, 10, 16, 8, 4, 9);
System.out.println(stream.anyMatch(s -> s == 2));
// 输出
// false
stream = Stream.of(3, 1, 10, 16, 8, 4, 9);
System.out.println(stream.allMatch(s -> s >= 1));
// 输出
// true
stream = Stream.of(3, 1, 10, 16, 8, 4, 9);
System.out.println(stream.noneMatch(s -> s >= 17));
// 输出
// true

附录:

stream 相关的类位于 java.util.stream 包中

InInterfaces

Classes

Enums

参考链接

https://blog.csdn.net/justloveyou_/article/details/79562574

https://blog.csdn.net/MinggeQingchun/article/details/123184273

https://blog.csdn.net/A_art_xiang/article/details/124644564

https://blog.csdn.net/qq_43842093/article/details/128526177