如何使用Collectors.groupingBy创建嵌套Map?

时间:2018-02-07 13:29:40

标签: java java-8 java-stream

我有一个班级列表ProductDto

public class ProductDto {
    private String Id;
    private String status;
    private Booker booker;
    private String category;
    private String type;
}

我想要一张如下地图: -

Map<String,Map<String,Map<String,Booker>>

属性将映射如下:

Map<status,Map<category,Map<type,Booker>

我知道使用Collectors.groupingBy可以轻松完成一个级别的分组。

我试图将它用于嵌套级别,但是当我为关键字段开始出现相同的值时,它失败了。

我的代码如下: -

 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;
                        })
                ));

如果有人知道通过使用流来做到这一点的好方法,请分享!

1 个答案:

答案 0 :(得分:16)

摘要/简要讨论

从面向对象的角度看,有一张地图地图的地图是有问题的,因为你可能看起来缺少一些抽象(即你可以创建一个封装结果的类Result嵌套分组)但是,当完全从纯数据导向方法考虑时,它是完全合理的。

所以在这里我提出了两种方法:第一种方法纯粹是面向数据的(使用嵌套的groupingBy调用,因此嵌套映射),而第二种方法更加符合OO,并且在抽象方面做得更好。分组标准。只需选择一个更能代表您的意图和编码标准/传统,更重要的是您最喜欢的那个。

面向数据的方法

对于第一种方法,您可以嵌套groupingBy来电:

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)组合。

此外,由于您需要Booker个实例而不是ProductDto个实例,我 调整最后一个groupingBy收集器,以便它返回{{ 1}}而不是Booker s。

关于减少

如果您只需要一个productDto个实例而不是Booker作为最内层地图的值,则需要一种方法来 reduce {{1}实例,即通过关联操作将多个实例转换为一个实例(累积某些属性的总和是最常见的一个)。

面向对象的友好方法

对于第二种方法,拥有List<Booker>可能被视为不良做法甚至是纯粹的邪恶。因此,您可以只拥有一个列表映射,而不是拥有列表映射映射的映射,其中的键表示要分组的3个属性的组合。

最简单的方法是使用Booker作为密钥,因为列表已经提供Map<String, Map<String, Map<String, List<Booker>>>>List个实现:

hashCode

如果您使用的是Java 9+,则可以使用List.of代替equals,因为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()))))); 会返回完全不可变且高度优化的列表。