繁体   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>>>

我有以下 class:

 public class SomeObject {

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

以及以下用例:

字段值不是唯一的。 我有一个 SomeObject 的列表。 首先我想知道哪些 SomeOjects 共享相同的 parentId,其次哪些共享相同的类型。 首先,我想将它们分组为以下结构:

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

第一个 map 的键是 parentId,值是另一个 map。 第二个 map 的键是类型,第二个 map 的值是来自 SomeObjects 的 id 列表。

我能够做到这一点,如下所示:

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

现在是我需要帮助的部分:

我现在想过滤这个创建的 map 如下:

假设我有 3 个 parentId 键,每个键都有一个 map 和两个键:type1 和 type2。 (和 2 个 id 列表作为值)

如果 type2 的 id 列表比 type1 的 id 列表包含更多/更少/不同的 id,那么我想删除/过滤掉它们的 parentId 条目。 我想为每个 parentId 这样做。

流有什么办法可以干净地实现这一目标吗?

在回答之前我会使用一些要点来改进代码:

  • 我会在 map 中留下 SomeObject 指针而不是字符串文字。 它们不仅在大多数情况下效率更高(固定 8 个字节与 2 个字符字节字符串),而且访问数据也更方便。

  • 我还将 String 类型设为枚举类型。

但说到问题的核心,我很抱歉。

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']
    }
}
    

像这样的东西

所以对于过滤本身

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

这段代码可以工作。 我省略了建筑/印刷,以免让它变得更大。 试试看

如果您只需要保留每种类型具有相同 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:

{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