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日
许可协议