简体   繁体   English

Java Stream 比较和过滤来自 Map 的条目<string, map<string, list<string> &gt;&gt;</string,>

[英]Java Stream compare and filter entries from Map<String, Map<String, List<String>>>

I have the following class:我有以下 class:

 public class SomeObject {

 private String id;
 private String parentId;
 private String type;
 
 //constructor,getters,setters
 }

And the following use case:以及以下用例:

The field values are not unique.字段值不是唯一的。 I have a List of SomeObject.我有一个 SomeObject 的列表。 First I want to know which SomeOjects share the same parentId and secondly which of those share the same type.首先我想知道哪些 SomeOjects 共享相同的 parentId,其次哪些共享相同的类型。 First I wanted to group them into the following structure:首先,我想将它们分组为以下结构:

 Map<String, Map<String, List<String>>>

The key of the first map is the parentId and the value is another map.第一个 map 的键是 parentId,值是另一个 map。 The key of the second map is the type and the value of the second map is a list of ids from the SomeObjects.第二个 map 的键是类型,第二个 map 的值是来自 SomeObjects 的 id 列表。

I was able to do this as follows:我能够做到这一点,如下所示:

    Map<String, Map<String, List<String>>> firstTry =
    SomeObjects.stream()
        .collect(
            groupingBy(
                SomeObject::getParentId,
                groupingBy(
                    SomeObject::getType,
                    mapping(SomeObject::getId, toList()))));

And now comes the part where I need some help:现在是我需要帮助的部分:

I now want to filter this created map as follows:我现在想过滤这个创建的 map 如下:

Lets assume I have 3 parentId keys which each then have a map with two keys: type1 and type2.假设我有 3 个 parentId 键,每个键都有一个 map 和两个键:type1 和 type2。 (and 2 lists of ids as values) (和 2 个 id 列表作为值)

If the list of ids from type2 contains more/less/different ids than the list of ids from type1, then I want to delete/filter out their parentId entry.如果 type2 的 id 列表比 type1 的 id 列表包含更多/更少/不同的 id,那么我想删除/过滤掉它们的 parentId 条目。 And I want to do that for each parentId.我想为每个 parentId 这样做。

Is there any way with streams to cleanly achieve this?流有什么办法可以干净地实现这一目标吗?

Some points I would use to improve the code before the answere:在回答之前我会使用一些要点来改进代码:

  • I would leave the SomeObject pointer instead of the String literal in the map.我会在 map 中留下 SomeObject 指针而不是字符串文字。 Not only they are gonna be more memory efficient most of the time (8 bytes fixed vs 2*character bytes String) but also much more convenient to access the data.它们不仅在大多数情况下效率更高(固定 8 个字节与 2 个字符字节字符串),而且访问数据也更方便。

  • I would also make the type String an enum type.我还将 String 类型设为枚举类型。

But getting to the core of the question, i'm sorry.但说到问题的核心,我很抱歉。

Lets assume I have 3 parentId keys which each then have a map with two keys: type1 and type2. (and 2 lists of ids as values)

{
    'p1': {
         'type1':['1','2','uuid-random-whatever'],
         'type2':['asdsad']
    },
    'p2': {
         'type1':['1','2'],
         'type2':['asdsad','i','want','more','power']
    },
    'p3': {
         'type1':['2'],
         'type2':['2']
    }
}
    

Something like this像这样的东西

So for the filtering itself所以对于过滤本身

        Map<String, Map<String, List<String>>> theMap
                = buildMapExample();

        Predicate<Map.Entry<String, Map<String, List<String>>>> filterType2LessElementsType1 =
                (Map.Entry<String, Map<String, List<String>>> entry) ->
                        entry.getValue().get("type2").size() < entry.getValue().get("type1").size();

        Predicate<Map.Entry<String, Map<String, List<String>>>> filterType2MoreElementsType1 =
                (Map.Entry<String, Map<String, List<String>>> entry) ->
                        entry.getValue().get("type2").size() > entry.getValue().get("type1").size();

        Predicate<Map.Entry<String, Map<String, List<String>>>> filterType2SameElementsType1 =
                (Map.Entry<String, Map<String, List<String>>> entry) ->
                        (new HashSet<>(entry.getValue().get("type2")))
                                .equals(new HashSet<>(entry.getValue().get("type1")));

        theMap = theMap.entrySet().stream()
                .filter(
                        // Choose your lambda for more/less/different. for example
                        filterType2LessElementsType1
                )
                .collect(
                        Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)
                );

        printMap(theMap);

This code would work.这段代码可以工作。 I left out the building/printing to not make this larger as it should.我省略了建筑/印刷,以免让它变得更大。 Try it out试试看

If you need to retain only those parentId which have the same ids per type, it can be done by converting lists of ids into set and checking the set size:如果您只需要保留每种类型具有相同 id 的parentId ,可以通过将 id 列表转换为集合并检查集合大小来完成:

List<SomeObject> list = Arrays.asList(
    new SomeObject("id1", "p1", "type1"),
    new SomeObject("id1", "p1", "type2"),

    new SomeObject("id2", "p2", "type1"),
    new SomeObject("id3", "p2", "type1"),
    new SomeObject("id2", "p2", "type2"),
            
    new SomeObject("id4", "p3", "type1"),
    new SomeObject("id4", "p3", "type2"),
    new SomeObject("id5", "p3", "type2")
);
//.. building firstTry as in the initial code snippet

System.out.println(firstTry);

firstTry.entrySet().stream()
        .filter(e -> new HashSet(e.getValue().values()).size() == 1)
        .forEach(System.out::println);

Output: Output:

{p1={type2=[id1], type1=[id1]}, p2={type2=[id2], type1=[id2, id3]}, p3={type2=[id4, id5], type1=[id4]}}
p1={type2=[id1], type1=[id1]}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM