记录下 Java Stream 的一些高效操作

我们日常在代码里处理一些集合逻辑的时候用到 Stream 其实还挺多的,普通的取值过滤集合一般都是结合 ide 的提示就能搞定了,但是有些不太常用的就在这记录下,争取后面都更新记录下来。

自定义 distinctByKey 对结果进行去重

stream 中自带的 distinct 只能对元素进行去重
比如下面代码

1
2
3
4
5
6
7
8
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(1);
list.add(2);
list = list.stream().distinct().collect(Collectors.toList());
System.out.println(list);
}

结果就是去了重的

1
[1, 2]

但是当我的元素是个复杂对象,我想根据对象里的某个元素进行过滤的时候,就需要用到自定义的 distinctByKey 了,比如下面的想对 userId 进行去重

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
public static void main(String[] args) {
List<StudentRecord> list = new ArrayList<>();
StudentRecord s1 = new StudentRecord();
s1.setUserId(11L);
s1.setCourseId(100L);
s1.setScore(100);
list.add(s1);
StudentRecord s2 = new StudentRecord();
s2.setUserId(11L);
s2.setCourseId(101L);
s2.setScore(100);
list.add(s2);
StudentRecord s3 = new StudentRecord();
s3.setUserId(12L);
s3.setCourseId(100L);
s3.setScore(100);
list.add(s3);
System.out.println(list.stream().distinct().collect(Collectors.toList()));
}
@Data
static class StudentRecord {
Long id;
Long userId;
Long courseId;
Integer score;
}

结果就是

1
[StudentRecord(id=null, userId=11, courseId=100, score=100), StudentRecord(id=null, userId=11, courseId=101, score=100), StudentRecord(id=null, userId=12, courseId=100, score=100)]

因为对象都不一样,所以就没法去重了,这里就需要用

1
2
3
4
5
6
public static <T> Predicate<T> distinctByKey(
Function<? super T, ?> keyExtractor) {

Map<Object, Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}

然后就可以用它来去重了

1
System.out.println(list.stream().filter(distinctByKey(StudentRecord::getUserId)).collect(Collectors.toList()));

看下结果

1
[StudentRecord(id=null, userId=11, courseId=100, score=100), StudentRecord(id=null, userId=12, courseId=100, score=100)]

但是说实在的这个功能感觉应该是 stream 默认给实现的

使用 java.util.stream.Collectors#groupingBy 对 list 进行分组

这个使用场景还是蛮多的,上面的场景里比如我要对 userId 进行分组,就一行代码就解决了

1
System.out.println(list.stream().collect(Collectors.groupingBy(StudentRecord::getUserId)));

结果

1
{11=[StudentRecord(id=null, userId=11, courseId=100, score=100), StudentRecord(id=null, userId=11, courseId=101, score=100)], 12=[StudentRecord(id=null, userId=12, courseId=100, score=100)]}

很方便的变成了以 userId 作为 key,以相同 userIdStudentRecordList 作为 valuemap 结构