如何使用Collectors.groupingBy创建嵌套地图? [英] How to create a nested Map using Collectors.groupingBy?
问题描述
我有一个班级列表,例如ProductDto
I have a list of class say ProductDto
public class ProductDto {
private String Id;
private String status;
private Booker booker;
private String category;
private String type;
}
我想要一个地图,如下所示:-
I want to have a Map as below:-
Map<String,Map<String,Map<String,Booker>>
属性将按以下方式映射:
The properties are to be mapped as below:
Map<status,Map<category,Map<type,Booker>
我知道使用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!
推荐答案
摘要/简短讨论
从面向对象的角度来看,拥有地图的地图是有问题的,因为似乎您缺乏某种抽象(即,您可以创建一个类Result
,该类封装了嵌套分组的结果) ).但是,仅从纯面向数据的方法考虑时,这是完全合理的.
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<String, Map<String, Map<String, List<Booker>>>>
.这是因为可能有多个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>
作为最里面的映射的值,则需要一种方法来 reduce 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>>>>
可能被视为不良行为,甚至被视为纯粹的邪恶.因此,您可能只有一个列表,而其键代表要分组的三个属性的组合,而不是列表列表的映射.
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.
这篇关于如何使用Collectors.groupingBy创建嵌套地图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!