Java 8中的Map流处理

3

我有一个嵌套的地图,我想要对其进行流处理:

Map<LocalDate, Map<Integer, List<String>>> mainMap;

并且移除与另一个映射匹配的值,

Map<LocalDate, List<String>> childMap;

例如,mainMap包含

{2016-02-23={120=[1,2,3], 121=[1,2,3], 122=[1,2,3], 123=[1,2,3]}}

并且 childMap 包含

{2016-02-23=[120,123]}

我想从mainMap中删除这些值。
预期输出 =>
{2016-02-23={121=[1,2,3], 122=[1,2,3]}}

如何在Java 8中实现?

1
如果childMap{2016-02-23=[120,121,122,123]},输出会是什么?我的意思是...外部映射的条目会发生什么?它必须是一个空映射还是您必须删除映射到空内部映射的条目? - fps
2个回答

4
您可以使用以下方法在原地进行操作:
mainMap.forEach((k, v) -> 
    v.keySet().removeIf(s -> 
        Optional.ofNullable(childMap.get(k)).map(o -> o.contains(s.toString())).orElse(false)
    )
);

这个代码会遍历mainMap。然后,针对该映射的值(一个Map<Integer, List<String>>),它将删除所有整数键(转换为String)其中childMap指向包含该键的列表的当前日期。请注意,如果子映射不包含当前日期的列表,则不会删除整数键。
下面是一个完整的示例代码,它会打印所需的输出结果:
public static void main(String[] args) {
    Map<LocalDate, Map<Integer, List<String>>> mainMap = new HashMap<>();

    Map<Integer, List<String>> map = new HashMap<>();
    map.put(120, Arrays.asList("1", "2", "3"));
    map.put(121, Arrays.asList("1", "2", "3"));
    map.put(122, Arrays.asList("1", "2", "3"));
    map.put(123, Arrays.asList("1", "2", "3"));
    mainMap.put(LocalDate.of(2016, 2, 23), map);

    Map<LocalDate, List<String>> childMap = new HashMap<>();
    childMap.put(LocalDate.of(2016, 2, 23), Arrays.asList("120", "123"));

    mainMap.forEach((k, v) -> 
        v.keySet().removeIf(s -> 
            Optional.ofNullable(childMap.get(k)).map(o -> o.contains(s.toString())).orElse(false)
        )
    );

    System.out.println(mainMap);
}

1
如果childMap是{2016-02-23=[120,121,122,123]},输出会是什么?我的观点是...外层映射的条目会发生什么?它必须是一个空映射还是您必须删除映射到空内部映射的条目?(我向OP提出了同样的问题) - fps
2
@FedericoPeraltaSchaffner 这是一个好问题。OP没有指定这种情况。我的答案将产生一个空的内部映射表。在 forEach 之后,您可以通过调用 mainMap.values().removeIf(Map::isEmpty); 来考虑这一点。 - Tunaki

2

除了Tunaki的答案,还有以下替代方案:

mainMap.forEach((k, v) -> childMap.getOrDefault(k, emptyList())
    .stream().map(Integer::valueOf).forEach(v::remove));

区别在于Tunaki的解决方案将遍历mainMap中包含的子地图的内容,并对每个内容执行childMap上的查找,而上面的解决方案将遍历childMap中找到的列表,并在mainMap的子地图中进行查找。当子地图和列表的大小增长时,这种方法更可取。
有一件事还没有解决,即如果由于操作而清空了mainMap的子地图,是否应该删除其映射。当迭代要修改的映射时,不可能一次性支持外部映射的删除(除非您使用旧循环并使用Iterator)。一种解决方案是先迭代childMap
childMap.forEach((k,v) -> mainMap.computeIfPresent(k, (d,m) -> {
    v.stream().map(Integer::valueOf).forEach(m::remove);
    return m.isEmpty()? null: m;
}));

请注意,大多数复杂性来自于两个映射之间的类型不匹配,因此必须执行childMapStringmainMapInteger的转换。如果它们具有相同的类型,例如如果childMapMap<LocalDate,List<Integer>>mainMapMap<LocalDate,Map<String,List<String>>>,则不删除空映射的解决方案就像这样简单:
mainMap.forEach((k, v) -> v.keySet().removeAll(childMap.getOrDefault(k, emptyList())));

并且这个解决方案还会从外部地图中删除空映射:

childMap.forEach((k,v) -> mainMap.computeIfPresent(k, (d,m) -> {
    m.keySet().removeAll(v);
    return m.isEmpty()? null: m;
}));

上面的代码可能会删除最初为空的映射。如果你只想在此操作期间删除映射,即不触及最初为空的映射,则代码变得更简单:
childMap.forEach((k,v) ->
    mainMap.computeIfPresent(k, (d,m) -> m.keySet().removeAll(v) && m.isEmpty()? null: m));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接