StreamAPI用法

学习利用StreamAPI对流数据进行处理

1. StreamAPI优势

1.1.代码简洁可读

它提供了一种简单、灵活、可读性极高的方式来处理集合和数组中的元素,使得代码更加简洁、高效和易于维护。

1.2.面对大数据量性能更优

当我们在处理大量数据(百万级以上),可以采用并行流(parallelStream)来优化数据处理的性能。

在使用原始的for循环处理、普通流、基本流和并行流处理数据时,他们的性能对比如下:

  • 小数据量

    for > 基本流 > 普通流 > 并行流

    虽然性能在小数据量时并占优势,但实际相差很少

  • 大数据量

    并行流 > for > 基本流 > 普通流

    当数据量很大时,并行流对于for更有优势,但要注意此时使用串行流会导致性能降低!

2.基本用法

1
2
3
4
5
6
7
8
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Stu {
private String name;
private int score;
private String room;
}
1
2
3
4
5
6
7
8
9
public class MyTest {
@Test
public void test() {
List<Stu> list = new ArrayList<>();
list.add(new Stu("张三", 100,"101"));
list.add(new Stu("李四", 110,"101"));
list.add(new Stu("王五", 80,"102"));
}
}

过滤

1
2
//过滤出分数大于100的学生
list.stream().filter(stu -> stu.getScore()>100).forEach(System.out::println);

映射

1
2
3
//取出所有学生的分数
List<Integer> collect = list.stream().map(Stu::getScore).collect(Collectors.toList());
System.out.println(collect);

降维

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
List<List<Stu>> lists = new ArrayList<>();

List<Stu> list1 = new ArrayList<>();
list1.add(new Stu("张三", 100,"101"));
list1.add(new Stu("李四", 110,"101"));
list1.add(new Stu("王五", 80,"102"));

List<Stu> list2 = new ArrayList<>();
list1.add(new Stu("张三2", 90,"101"));
list1.add(new Stu("李四2", 30,"101"));
list1.add(new Stu("王五2", 120,"102"));

lists.add(list1);
lists.add(list2);

lists.stream().flatMap(list -> list.stream()).forEach(System.out::println);


//输出结果:
Stu(name=张三, score=100, room=101)
Stu(name=李四, score=110, room=101)
Stu(name=王五, score=80, room=102)
Stu(name=张三2, score=90, room=101)
Stu(name=李四2, score=30, room=101)
Stu(name=王五2, score=120, room=102)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Integer[][] array2D = {
{1,2,3},
{6,8,9},
{7,4,5}
};
Arrays.stream(array2D)
.flatMap(array->Arrays.stream(array))
.forEach(System.out::println);

//输出结果
1
2
3
6
8
9
7
4
5

截取

1
2
//只取前2条数据
list.stream().limit(2).forEach(System.out::println);
1
2
//跳过第1条数据
list.stream().skip(1).forEach(System.out::println);

查找

1
2
3
4
5
6
7
8
9
//找到第一个符合条件的,否则返回null
System.out.println(
list.stream().filter(stu -> stu.getScore() > 99).findFirst().orElse(null)
);

//找到任意一个符合条件的,否则返回null,只有在并行流下才和findFirst()有区别
System.out.println(
list.stream().filter(stu -> stu.getScore() > 99).findAny().orElse(null)
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//只要有一个满足就为true
boolean b1 = list.stream().anyMatch(stu -> stu.getScore() > 100);
//所有都要满足才为true
boolean b2 = list.stream().allMatch(stu -> stu.getScore() > 100);
//所有都不满足才为true
boolean b3 = list.stream().noneMatch(stu -> stu.getScore() > 100);
System.out.println(b1);
System.out.println(b2);
System.out.println(b3);

//结果
true
false
false

去重

1
list.stream().distinct().forEach(System.out::println);

排序

1
2
3
4
//根据分数降序
list.stream().sorted(
Comparator.comparing(Stu::getScore).reversed()
).forEach(System.out::println);

求值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//常用求值
//需要先将普通流转为Integer基本流
//最大值
list.stream().mapToInt(Stu::getScore).max();
//最小值
list.stream().mapToInt(Stu::getScore).min();
//平均值
list.stream().mapToInt(Stu::getScore).average();
//总数
list.stream().mapToInt(Stu::getScore).count();
//总和
list.stream().mapToInt(Stu::getScore).sum();
//综合
IntSummaryStatistics intSummaryStatistics
= list.stream().mapToInt(Stu::getScore).summaryStatistics();
System.out.println(intSummaryStatistics.getMax());
System.out.println(intSummaryStatistics.getMin());
System.out.println(intSummaryStatistics.getAverage());
System.out.println(intSummaryStatistics.getCount());
System.out.println(intSummaryStatistics.getSum());

收集

1
2
3
4
5
6
7
//收集为list
List<Stu> collect = list.stream().collect(Collectors.toList());
//收集为set
Set<Stu> collect = list.stream().collect(Collectors.toSet());
//收集为map
Map<String, Integer> collect =
list.stream().collect(Collectors.toMap(Stu::getName, Stu::getScore));

分组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Map<String, List<Stu>> collect
= list.stream()
//根据教室分组,将学生信息保存在List中
.collect(Collectors.groupingBy(Stu::getRoom, Collectors.toList()));


Map<String, List<Integer>> collect
= list.stream()
.collect(
Collectors.groupingBy(
//根据教室分组
Stu::getRoom,
//只保留分数信息在一个List中
Collectors.mapping(Stu::getScore, Collectors.toList())
)
);

并行流

1
2
3
//有以下两种方法来获取并行流
list.parallelStream().forEach(System.out::println);
list.stream().parallel().forEach(System.out::println);

3.三种基本流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//三种基本流
IntStream a = IntStream.of(97,98,99);
LongStream b = LongStream.of(1L,2L,3L);
DoubleStream c = DoubleStream.of(1.0,2.0,3.0);
//普通流/包装流
Stream<Integer> stream = Stream.of(1,2,3);

//流的转换
IntStream intStream = stream.mapToInt(i -> i);
DoubleStream doubleStream = stream.mapToDouble(i -> i);
LongStream longStream = stream.mapToLong(i -> i);

Stream<Integer> boxed = a.boxed();
Stream<Long> boxed1 = b.boxed();
Stream<Double> boxed2 = c.boxed();

4.流的特性

一次使用

一次使用是指,在一个流调用了终结方法后,无法再次使用。

两类操作

两类操作是指,中间操作和终结操作。

5.使用案例

找出每个教室中分数最高的前2人

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class MyTest {
public static void main(String[] args) {
List<Stu> list = new ArrayList<>();
list.add(new Stu("张三", 100,"101"));
list.add(new Stu("李四", 110,"103"));
list.add(new Stu("王五", 80,"101"));
list.add(new Stu("赵六", 90,"105"));
list.add(new Stu("孙七", 70,"105"));
list.add(new Stu("钱八", 60,"106"));
list.add(new Stu("李九", 50,"105"));
list.add(new Stu("赵十", 40,"104"));
list.add(new Stu("孙十一", 30,"105"));
list.add(new Stu("钱十二", 20,"104"));
list.add(new Stu("李十三", 10,"106"));
list.add(new Stu("赵十四", 15,"106"));
list.add(new Stu("孙十五", 65,"107"));
list.add(new Stu("钱十六", 78,"103"));
list.add(new Stu("李十七", 96,"107"));
list.add(new Stu("赵十八", 30,"106"));
list.add(new Stu("孙十九", 28,"102"));
list.add(new Stu("钱二十", 102,"101"));
list.add(new Stu("孙二十一", 98,"102"));
list.add(new Stu("钱二十三", 66,"102"));

//找出每个教室中分数最高的前2人
Map<String, Map<String, Integer>> collect = list.stream()
.collect(
//分组
Collectors.groupingBy(
//分组参数-分组依据:根据教室分组
Stu::getRoom,
//分组参数-容器
Collectors.collectingAndThen(//分组后继续操作
//每个组结果只获取学生姓名和分数
Collectors.toMap(Stu::getName, Stu::getScore),
//组内排序
map->map.entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.limit(2)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
)
)
);

collect.entrySet().stream().forEach(System.out::println);
}
}

统计各班人数

1
2
3
4
//统计各班人数
list.stream()
.collect(Collectors.groupingBy(Stu::getRoom, Collectors.counting()))
.entrySet().stream().forEach(System.out::println);