Stream流式编程思想及常用API介绍
Stream流像一个流水线,可以理解为将一车对象,放入流水线中,流水线一环一环进行处理。
提示
本文可能需要你先具对备函数式接口和方法引用相关内容的了解。
# 概念
# 中间操作、终止操作、惰性求值
中间操作返回一个流,所以可以连接多个流操作。
终止操作结束流,返回一个void或非流结果。
中间操作的调用相当于对流水线的拼装,只有当终止操作调用时对象才会开始真正流入。
Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
System.out.println("filter: " + s);
return true;
});
//由于没有终止操作,filter并没有被执行,所以没有输出。
2
3
4
5
6
# 顺序流和并行流
顺序流:对象一个一个流入一个条流水线。
并行流:对象一起流入多条流水线。
# 常用API
# stream 构建流
所有的Collection都可以通过.stream创建一个流。
也可以通过Stream.of方法创建流。
ArrayList<Integer> integers = new ArrayList<>();
integers.add(7);
integers.add(3);
integers.add(5);
integers.add(1);
integers.add(2);
integers.add(3);
Stream<Integer> stream = integers.stream();
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
2
3
4
5
6
7
8
9
10
# parallel 并行流
stream.parallel() 将流转为并行流。
提示
他的调用顺序不重要,因为他不像其他的方法对流做拼接,而是改变这个流的一个标记,最后会根据这个标记执行并行还是串行。
# 中间操作
中间操作相当于对流的拼接,由流对象调用,返回的还是流对象。对象会流入环节再流出到下一个,调用顺序决定拼接顺序。
# filter 过滤
接收一个对象流入,接受一个布尔值返回,扔掉false的对象,放行为true的对象。
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
integerStream.filter(i -> i > 3).forEach(System.out::print);
//45
2
3
# map 转换
接收一个对象流入,流出一个对象。
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
integerStream.map(i -> "数字" + i).forEach(System.out::print);
// 数字1数字2数字3数字4数字5
2
3
# flatMap 扁平化
将一个对象拆分成流,再把拆分出的流的流出流汇总到一起流出。
ArrayList<List<Integer>> arrayList = new ArrayList<>();
ArrayList<Integer> integers1 = new ArrayList<>();
ArrayList<Integer> integers2 = new ArrayList<>();
integers1.add(1);
integers1.add(2);
integers1.add(3);
integers2.add(4);
integers2.add(5);
integers2.add(6);
arrayList.add(integers1);
arrayList.add(integers2);
arrayList.stream()
.flatMap(a -> a.stream().skip(1))
.forEach(System.out::print);
//2356
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# distinct 去重
Stream<Integer> integerStream = Stream.of(1, 2, 2, 2, 5);
integerStream.distinct().forEach(System.out::print);
// 125
2
3
# limit 截断流
接收一个int值,当流入对象够数后停止流入
Stream<Integer> integerStream = Stream.of(1, 2, 2, 2, 5);
integerStream.limit(2).forEach(System.out::print);
// 12
2
3
# skip 跳过
接受一个int值,抛弃前几个流入的对象
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
integerStream.skip(3).forEach(System.out::print);
// 45
2
3
# sorted 排序
接收两个对象,排序。
Stream<Integer> integerStream = Stream.of(4, 2, 1, 3);
integerStream.sorted((i1, i2) -> i2 - i1).forEach(System.out::print);
//4321
2
3
顺序问题
当[1,2]经过流abcd时,顺序是a1-b1-c1-d1 a2-b2-c2-d2。 但因为sorted是把多个对象变得有序,所以需要等对象全部流入后处理,完成后再一个个流出。 所以中间有排序s,即abscd,会变成a1-b1 a2-b2 s1,2 c1-d1 c2-d2。
# 终止操作
终止操作返回void或非流对象,他的后边不再拼接其他操作,只有终止操作被拼接时,流才会开始流入对象。
# forEach 遍历
返回值void,消费每一个对象。
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
integerStream.forEach(System.out::print);
// 12345
2
3
# findAny findFirst
当一个对象流入时,终止流并返回对象。 findAny在并行时不能保证初始顺序,findFirst可以。
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
Optional<Integer> any = integerStream.findAny();
System.out.println(any.get());
// 1
2
3
4
# collect 收集器
# 数据类型转换
Stream<Integer> integerStream = Stream.of(4, 2, 1, 3);
Set<Integer> set = integerStream.collect(Collectors.toSet());
2
# 分组
接受一个传入参数,传出结果的方法,根据结果分组。
根据布尔结果分组。
Stream<Integer> integerStream = Stream.of(4, 2, 1, 3, 3, 4, 2);
Map<Boolean, List<Integer>> collect = integerStream.collect(Collectors.groupingBy(a -> a > 3));
System.out.println(collect);
//{false=[2, 1, 3], true=[4]}
2
3
4
根据字符串分组。
public class DemoApplication {
public static void main(String[] args) {
Stream<Integer> integerStream = Stream.of(4, 2, 1, 3, 3, 4, 2);
Map<String, List<Integer>> collect =
integerStream.collect(Collectors.groupingBy(DemoApplication::get));
System.out.println(collect);
// {大=[4, 4], 中=[3, 3], 小=[2, 1, 2]}
}
static String get(Integer i) {
if (i > 3) {
return "大";
}
if (i == 3) {
return "中";
}
return "小";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# reduce 归约
接收一个初始值,两个传入值,处理后返回结果。
Stream<Integer> integerStream = Stream.of(1,2,3);
Integer integer = integerStream.reduce(0,(a, b) -> a + b);
System.out.println(integer);
//6
2
3
4
# sum 求和
相当于 reduce(0, Integer::sum)
# count 计数
返回流到终点的对象数。