如何使用 Collectors.groupingBy 创建嵌套地图?
我有一个类列表说 ProductDto
public class ProductDto {
private String Id;
private String status;
private Booker booker;
private String category;
private String type;
}
我想要一张如下图:-
地图
属性映射如下:
地图
我知道使用 Collectors.groupingBy
可以轻松完成一级分组,没有任何麻烦.
I know one level of grouping could be done easily without any hassles using Collectors.groupingBy
.
我尝试将它用于嵌套级别,但是当作为键的字段开始出现相同的值时,它对我来说失败了.
I tried to use this for nested level but it failed for me when same values started coming for fields that are keys.
我的代码如下:-
list.stream()
.collect(Collectors.groupingBy(
(FenergoProductDto productDto) ->
productDto.getStatus()
,
Collectors.toMap(k -> k.getProductCategory(), fProductDto -> {
Map<String, Booker> productTypeMap = new ProductTypes();
productTypeMap.put(fProductDto.getProductTypeName(),
createBooker(fProductDto.getBookingEntityName()));
return productTypeMap;
})
));
如果有人知道使用流的好方法,请分享!
If anyone knows a good approach to do this by using streams, please share!
推荐答案
摘要/简要讨论
从面向对象的角度来看,拥有地图的地图是有问题的,因为您似乎缺少一些抽象(即您可以创建一个封装了嵌套分组的结果).但是,仅从纯粹的面向数据的方法考虑时,它是完全合理的.
Abstract / Brief discussion
Having a map of maps of maps is questionable when seen from an object-oriented prespective, as it might seem that you're lacking some abstraction (i.e. you could create a class Result
that encapsulates the results of the nested grouping). However, it's perfectly reasonable when considered exclusively from a pure data-oriented approach.
所以在这里我提出了两种方法:第一种是纯粹面向数据的(使用嵌套的 groupingBy
调用,因此是嵌套的映射),而第二种方法对 OO 更友好,效果更好在抽象分组标准时.只需选择一个更能代表您的意图和编码标准/传统,更重要的是,选择您最喜欢的那个.
So here I present two approaches: the first one is purely data-oriented (with nested groupingBy
calls, hence nested maps), while the second one is more OO-friendly and makes a better job at abstracting the grouping criteria. Just pick the one which better represents your intentions and coding standards/traditions and, more importantly, the one you most like.
对于第一种方法,您可以嵌套 groupingBy
调用:
For the first approach, you can just nest the groupingBy
calls:
Map<String, Map<String, Map<String, List<Booker>>>> result = list.stream()
.collect(Collectors.groupingBy(ProductDto::getStatus,
Collectors.groupingBy(ProductDto::getCategory,
Collectors.groupingBy(ProductDto::getType,
Collectors.mapping(
ProductDto::getBooker,
Collectors.toList())))));
如您所见,结果是 Map
.这是因为可能有多个 ProductDto
实例具有相同的 (status, category, type)
组合.
As you see, the result is a Map<String, Map<String, Map<String, List<Booker>>>>
. This is because there might be more than one ProductDto
instance with the same (status, category, type)
combination.
另外,由于您需要 Booker
实例而不是 ProductDto
实例,我正在调整最后一个 groupingBy
收集器,以便它返回 Booker
s 而不是 productDto
s.
Also, as you need Booker
instances instead of ProductDto
instances, I'm adapting the last groupingBy
collector so that it returns Booker
s instead of productDto
s.
如果您只需要一个 Booker
实例而不是 List
作为最内层地图的值,则需要一种方法来减少 Booker
实例,即通过关联操作将许多实例转换为一个(累积某些属性的总和是最常见的).
If you need to have only one Booker
instance instead of a List<Booker>
as the value of the innermost map, you would need a way to reduce Booker
instances, i.e. convert many instances into one by means of an associative operation (accumulating the sum of some attribute being the most common one).
对于第二种方法,使用 Map<String, Map<String, Map<String, List<Booker>>>>
可能被视为不好的做法,甚至是纯粹的邪恶.因此,您可以只有一个列表映射,其键代表您要分组的 3 个属性的组合,而不是拥有列表映射的映射.
For the second approach, having a Map<String, Map<String, Map<String, List<Booker>>>>
might be seen as bad practice or even as pure evil. So, instead of having a map of maps of maps of lists, you could have only one map of lists whose keys represent the combination of the 3 properties you want to group by.
最简单的方法是使用 List
作为键,因为列表已经提供 hashCode
和 equals
实现:
The easiest way to do this is to use a List
as the key, as lists already provide hashCode
and equals
implementations:
Map<List<String>, List<Booker>> result = list.stream()
.collect(Collectors.groupingBy(
dto -> Arrays.asList(dto.getStatus(), dto.getCategory(), dto.getType()),
Collectors.mapping(
ProductDto::getBooker,
Collectors.toList())))));
如果您使用的是 Java 9+,则可以使用 List.of
而不是 Arrays.asList
,因为 List.of
返回一个完全不可变且高度优化列表.
If you are on Java 9+, you can use List.of
instead of Arrays.asList
, as List.of
returns a fully immutable and highly optimized list.
相关文章