Stream流技巧总结
# Stream 流技巧总结
# 抽取列表元素属性整合成列表集合
利用 stream 流的 map
方法:
// 获取List的【id集合】
List<Long> ids = list.stream().map(LearningLesson::getId).collect(Collectors.tolist());
// 获取Set的【id集合】
Set<Long> ids2 = list,stream().map(LearningLesson;:getId).collect(Collectors.toSet());
1
2
3
4
5
6
7
2
3
4
5
6
7
LearningLesson 是一个实体类。
# list 转 Map
1、传统写法 - new 一个 Map
Map<Long, LearningLesson> map = new HashMap<>();
for (LearningLesson lesson : list) {
map.put(lessson.getId(), lesson);
}
1
2
3
4
5
6
2
3
4
5
6
2、使用 stream 流
当要使用两个 for 循环的时候,这种方法可能可以用来提高性能。
// toMap 方法
Map<Long, LearningLesson> lessonMap = list.stream()
.collect(Collectors.toMap(LearningLesson::getId, c -> c));
// c -> c 等价于 Function.identity()
// Function.identity() 是 Java 8 引入的一个方法,它返回一个函数,该函数总是返回其输入参数。简单来说,它是一个用于创建恒等函数的静态方法。
Map<Long, LearningLesson> lessonMap = list.stream()
.collect(Collectors.toMap(LearningLesson::getId, Function.identity()));
// 其他用法
Map<Long, Long> lessonMap = list.stream()
.collect(Collectors.toMap(LearningLesson::getId, c -> c.getCourseId));
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
3、Collectors.groupingBy 方法
// 分组
Map<Long, List<LearningLesson>> lessonMap = list.stream()
.collect(Collectors.groupingBy(LearningLesson::getObjId));
1
2
3
2
3
⚠️注意:
- 需考虑
LearningLesson::getId
是否会有重复数据的情况 - 在使用
Collectors.groupingBy
进行分组时,如果被分组的属性(这里是LearningLesson::getObjId
)存在重复的情况,那么结果就会是将具有相同属性值的元素放入同一个列表中。 - 是会放入同一个列表中,而不是覆盖。
# filter 过滤
# 基本用法
# 过滤偶数
public class FilterExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 使用 filter 过滤出偶数
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// 打印结果
System.out.println("原始列表:" + numbers);
System.out.println("过滤后的偶数列表:" + evenNumbers);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 过滤对象
Long maintainerId = documentInfo.getObjId();
MaintainerEntity maintainer = list.stream()
.filter(m -> m.getId().equals(maintainerId))
.findFirst()
.orElse(null);
1
2
3
4
5
6
7
2
3
4
5
6
7
解释
list.stream()
: 将集合list
转换为一个流(Stream)对象,这样我们就可以使用流的各种操作。.filter(m -> m.getId().equals(maintainerId))
: 使用filter
操作,保留满足给定条件的元素。在这里,保留那些m
对象,其id
属性与给定的maintainerId
相等。.findFirst()
: 获取满足条件的第一个元素。这里,我们得到的是一个Optional
对象,因为可能找不到符合条件的元素。.orElse(null)
: 如果有符合条件的元素,返回该元素;否则,返回null
。这里的null
是作为默认值传递的。
总结
- 这段代码的目的是从
list
集合中找到一个具有指定maintainerId
的MaintainerEntity
对象。如果找到了,则返回该对象,否则返回null
。 - 这种写法的好处在于它简洁而流畅地表达了查找的过程,同时通过使用
Optional
类型避免了空指针异常,因为findFirst()
可能找不到匹配的元素。
# 结合 Predicate 使用
filter
方法接受一个 Predicate
参数,该参数是一个函数接口,用于定义过滤条件。我们可以将一个自定义的 Predicate
传递给 filter
方法,以实现更灵活的过滤条件。
例如,我们定义一个 Predicate
用于判断一个数字是否为质数:
public class FilterWithPredicate {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 定义 Predicate 判断是否为质数
Predicate<Integer> isPrime = n -> {
if (n <= 1) {
return false;
}
for (int i = 2; i <= Math.sqrt(n); i++) {
if (n % i == 0) {
return false;
}
}
return true;
};
// 使用 filter 过滤出质数
List<Integer> primeNumbers = numbers.stream()
.filter(isPrime)
.collect(Collectors.toList());
// 打印结果
System.out.println("原始列表:" + numbers);
System.out.println("过滤后的质数列表:" + primeNumbers);
}
}
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
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
# 去除查询结果中的重复元素
# 思路分析
- 首先使用
Stream.map()
方法将list
中的每个FormEntity
对象的type
字段提取出来, - 然后使用
Collectors.toSet()
方法将这些type
值收集到一个不包含重复元素的Set
集合中。 - 最后,再将这个
Set
集合转换回List
。
这样,返回的 uniqueTypes
列表就不会有重复的元素了。
# 代码
List<Integer> uniqueTypes = list.stream()
.map(FormEntity::getType)
.collect(Collectors.toSet())
.stream()
.collect(Collectors.toList());
1
2
3
4
5
2
3
4
5
# flatMap 用法解析
flatMap
接受一个函数作为参数,该函数将流中的每个元素转换为一个流。然后,它将这些生成的流“扁平化”,即把所有这些流中的元素连接成一个新的流。
示例:
List<String> allModelIds = accreditInfoByAppId.stream()
.flatMap(map -> Arrays.stream(map.get("modelIds").split(","))) // 将每个Map中的modelIds拆分为单独的字符串
.collect(Collectors.toList());
1
2
3
2
3
# 统计列表中某个元素出现的次数
// 写法一:统计每个信用代码出现的次数
Map<String, Long> creditCodeCountMap = importList.stream()
.map(EntImportExcelVO::getCreditCode)
.collect(Collectors.groupingBy(code -> code, Collectors.counting()));
// 写法二
Map<String, Long> creditCodeCountMap = new HashMap<>();
for (EntImportExcelVO vo : importList) {
String code = medicalEntImportExcelVO.getCreditCode();
creditCodeCountMap.merge(code, 1L, Long::sum);
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# bean 列表转换,预设初始容量
1、EnterpriseRespVO
中创建构造函数:
public EnterpriseRespVO() {}
// 这种写法,类似手动 set 属性,大数据量时会比 BeanUtil 写法的性能好很多
public EnterpriseRespVO(MarketEnterpriseDO enterpriseDO) {
this.id = String.valueOf(enterpriseDO.getId());
this.entName = enterpriseDO.getEntName();
this.creditCode = enterpriseDO.getCreditCode();
this.status = String.valueOf(enterpriseDO.getStatus());
// ...
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
2、使用 .map(EnterpriseRespVO::new)
进行转换
// 写法一:普通写法
public List<EnterpriseRespVO> convertToRespVOList(List<EnterpriseDO> enterpriseDOList) {
// 预设初始容量,减少扩容次数
return enterpriseDOList.stream()
.map(EnterpriseRespVO::new)
.collect(Collectors.toCollection(() -> new ArrayList<>(enterpriseDOList.size())));
}
// 写法二:泛型写法
/**
* 将DO对象集合转换为VO对象集合
*
* @param doList DO对象集合
* @param mapper 函数式接口, 示例: Function<EntDO, EntRespVO> mapper = EntRespVO::new;
*
* @return VO对象集合
* @param <D> DO对象
* @param <V> VO对象
*/
public static <D, V> List<V> convertToRespVOList(List<D> doList, Function<D, V> mapper) {
return doList.stream()
.map(mapper)
.collect(Collectors.toCollection(() -> new ArrayList<>(doList.size())));
}
// 使用示例
private List<AnotherVO> convertAnotherDOListToVOList(List<AnotherDO> anotherDOList) {
return convertToRespVOList(anotherDOList, AnotherVO::new);
}
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
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
上次更新: 2024/12/20 18:14:13