Stream流用法

一、概述

Stream 流是 Java 8 新提供给开发者的一组操作集合的 API,将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选、排序、聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由终端操作 (terminal operation) 得到前面处理的结果。Stream 流可以极大的提高开发效率,也可以使用它写出更加简洁明了的代码。

stream流特点

  • 不是数据结构,不会保存数据,只是在原数据集上定义了一组操作
  • 惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止操作的时候才会进行实际的计算
  • Stream不保存数据,它会将操作后的数据保存到另外一个对象中

Stream可以由数组或集合创建,对流的操作分为两种:

中间操作,每次返回一个新的流,可以有多个。(筛选filter、映射map、排序sorted、去重组合skip—limit)

终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。(遍历foreach、匹配find–match、规约reduce、聚合max–min–count、收集collect)

stream和parallelStream的简单区分

stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。如果流中的数据量足够大,并行流可以加快处速度。除了直接创建并行流,还可以通过parallel()把顺序流转换成并行流:

1
Optional<Integer> findFirst = list.stream().parallel().filter(x->x>6).findFirst();

二、stream流的常用方法

Stream流的常用方法:

终结方法:返回值类型不再是Stream接口本身类型的方法,例如:forEach方法和count方法

非终结方法/延迟方法:返回值类型仍然是Stream接口自身类型的方法,除了终结方法都是延迟方法。例如:filter,limit,skip,map,conat

方法名称 方法作用 方法种类 是否支持链式调用
count 统计个数 终结方法 否
forEach 逐一处理 终结方法 否
filter 过滤 函数拼接 是
limit 取用前几个 函数拼接 是
skip 跳过前几个 函数拼接 是
map 映射 函数拼接 是
concat 组合 函数拼接 是

示例

count方法

long count (); 统计流中的元素,返回long类型数据

1
2
3
4
5
6
7
8
9
10
11
List<String> list = new ArrayList<>();
list.add("张老三");
list.add("张小三");
list.add("李四");
list.add("赵五");
list.add("张六");
list.add("王八");

long count = list.stream().count();
System.out.println("集合中的元素个数是:" + count);
//集合中的元素个数是:6
filter方法

Stream filter(Predicate predicate); 过滤出满足条件的元素

参数Predicate:函数式接口,抽象方法:boolean test (T t)

Predicate接口:是一个判断接口

1
2
3
4
5
6
7
8
// 获取stream流
Stream<String> stream = Stream.of("张老三", "张小三", "李四", "赵五", "刘六", "王七");
// 需求:过去出姓张的元素
stream.filter((String name)->{
return name.startsWith("张");
}).forEach((String name)->{
System.out.println("流中的元素" + name);
});
forEach方法

void forEach(Consumer<? super T> action):逐一处理流中的元素

参数 Consumer<? super T> action:函数式接口,只有一个抽象方法:void accept(T t);

注意:

1.此方法并不保证元素的逐一消费动作在流中是有序进行的(元素可能丢失)

2.Consumer是一个消费接口(可以获取流中的元素进行遍历操作,输出出去),可以使用Lambda表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
List<String> list = new ArrayList<>();
list.add("张老三");
list.add("张小三");
list.add("李四");
list.add("赵五");
list.add("张六");
list.add("王八");

// 函数模型:获取流 --> 注意消费流中的元素
list.stream().forEach((String name)->{
System.out.println(name);
});

输出结果:
张老三
张小三
李四
赵五
张六
王八
limit方法

Stream limit(long maxSize); 取用前几个元素

注意:参数是一个long 类型,如果流的长度大于参数,则进行截取;否则不进行操作

1
2
3
4
5
6
7
8
9
10
11
// 获取流的长度
Stream<String> stream1 = Stream.of("张老三", "张小三", "李四", "赵五", "刘六", "王七");
// 需求:保留前三个元素
stream1.limit(3).forEach((String name)->{
System.out.println("流中的前三个元素是:" + name);
});

输出结果:
流中的前三个元素是:张老三
流中的前三个元素是:张小三
流中的前三个元素是:李四
map方法

Stream map(Function<? super T,? exception R> mapper;

参数Function<T,R>:函数式接口,抽象方法:R apply(T t);

Function<T,R>:其实就是一个类型转换接口(T和R的类型可以一致,也可以不一致)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 获取Stream流
Stream<String> stream1 = Stream.of("11","22","33","44","55");
// 需求:把stream1流中的元素转换为int类型
stream1.map((String s)->{
return Integer.parseInt(s); // 将String类型的s进行转换为Integer类型的元素,并返回
}).forEach((Integer i)->{
System.out.println(i); // 将转换后的int类型的元素逐一输出
});

输出结果:
11
22
33
44
skip方法

Stream skip(long n); 跳过前几个元素
注意:如果流的当前长度大于n,则跳过前n个,否则将会得到一个长度为0的空流

1
2
3
4
5
6
7
8
9
10
11
// 获取stream流
Stream<String> stream = Stream.of("张老三", "张小三", "李四", "赵五", "刘六", "王七");

stream.skip(3).forEach((String name)->{
System.out.println("跳过前三个,打印剩下的" + name);
});

输出结果:
跳过前三个,打印剩下的赵五
跳过前三个,打印剩下的刘六
跳过前三个,打印剩下的王七
concat方法

public static Stream concat(Stream<? extends T> a, Stream<? extends T> b) –> 合并两个流

1
2
3
4
5
6
7
8
9
10
11
Stream<String> stream1 = Stream.of("11","22","33","44","55");
Stream<String> stream2 = Stream.of("张颜宇", "张三", "李四", "赵五", "刘六", "王七");

// 需求:合并两个流
Stream<String> stream = Stream.concat(stream1,stream2);
stream.forEach((String name)->{
System.out.print(name);
});

输出结果:
1122334455张颜宇张三李四赵五刘六王七
distinct方法
sorted方法
anyMatch方法
allMatch方法
noneMatch方法
findFirst和findAny方法
max和min方法
reduce方法

三、收集Stream流

Stream流中提供了一个方法,可以把流中的数据收集到单例集合中

<R, A> R collect(Collector<? super T, A, R> collector); 把流中的数据手机到单列集合中

返回值类型是R。R指定为什么类型,就是手机到什么类型的集合

参数Collector<? super T, A, R>中的R类型,决定把流中的元素收集到哪个集合中

参数Collector如何得到 ?,可以使用 java.util.stream.Collectors工具类中的静态方法:

- public static Collector<T, ?, List> toList():转换为List集合

- public static Collector<T, ?, Set> toSet() :转换为Set集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
List<String> list2 = new ArrayList<>();
list2.add("张老三");
list2.add("张小三");
list2.add("李四");
list2.add("赵五");
list2.add("张六");
list2.add("王八");

// 需求:过滤出姓张的并且长度为3的元素
Stream<String> stream = list2.stream().filter((String name) -> {
return name.startsWith("张");
}).filter((String name) -> {
return name.length() == 3;
});

// stream 收集到单列集合中
List<String> list = stream.collect(Collectors.toList());
System.out.println(list);

// stream 手机到单列集合中
Set<String> set = stream.collect(Collectors.toSet());
System.out.println(set);

Stream流用法
https://xiong-hai.github.io/2023/09/28/Stream流用法/
作者
Xiong-Hai
发布于
2023年9月28日
许可协议