如何使用Collectors.groupingBy创建嵌套地图? [英] How to create a nested Map using Collectors.groupingBy?

查看:146
本文介绍了如何使用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 Bookers instead of productDtos.

如果只需要一个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作为键,因为列表已经提供了hashCodeequals的实现:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆