使用流和 lambda 将 Map<Integer、List<String>> 拼合到 Map<String、Integer>

2022-09-02 00:38:25

我想平展一个将键与列表相关联的,而不会丢失键映射。我很好奇,好像用 和 这样做是可能的和有用的。MapIntegerStringstreamlambda

我们从这样的东西开始:

Map<Integer, List<String>> mapFrom = new HashMap<>();

让我们假设 mapFrom 填充在某个地方,如下所示:

1: a,b,c
2: d,e,f
etc.

我们还假设列表中的值是唯一的。

现在,我想“展开”它以获得第二张地图,例如:

a: 1
b: 1
c: 1
d: 2
e: 2
f: 2
etc.

我可以像这样做(或者非常相似,使用):foreach

Map<String, Integer> mapTo = new HashMap<>();
for (Map.Entry<Integer, List<String>> entry: mapFrom.entrySet()) {
    for (String s: entry.getValue()) {
        mapTo.put(s, entry.getKey());
    }
}

现在让我们假设我想使用 lambda 而不是嵌套循环。我可能会做这样的事情:for

Map<String, Integer> mapTo = mapFrom.entrySet().stream().map(e -> {
    e.getValue().stream().?
    // Here I can iterate on each List, 
    // but my best try would only give me a flat map for each key, 
    // that I wouldn't know how to flatten.
}).collect(Collectors.toMap(/*A String value*/,/*An Integer key*/))

我也尝试了一下,但我不认为这是正确的方法,因为尽管它可以帮助我摆脱维度问题,但我在这个过程中失去了关键。flatMap

简而言之,我的两个问题是:

  • 是否可以使用并实现这一点?streamslambda
  • 这样做有用(性能,可读性)吗?

答案 1

您需要 使用 将值平展到新流中,但由于您仍然需要将原始键收集到 中,因此必须映射到保存键和值的临时对象,例如flatMapMap

Map<String, Integer> mapTo = mapFrom.entrySet().stream()
       .flatMap(e->e.getValue().stream()
                    .map(v->new AbstractMap.SimpleImmutableEntry<>(e.getKey(), v)))
       .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

是不存在的元组类型的替身,任何其他能够容纳两个不同类型的对象的类型就足够了。Map.Entry

不需要这些临时对象的替代方法是自定义收集器:

Map<String, Integer> mapTo = mapFrom.entrySet().stream().collect(
    HashMap::new, (m,e)->e.getValue().forEach(v->m.put(v, e.getKey())), Map::putAll);

这与以静默方式覆盖重复密钥不同,而没有合并函数,如果存在重复的密钥,则会引发异常。基本上,此自定义收集器是toMaptoMap

Map<String, Integer> mapTo = new HashMap<>();
mapFrom.forEach((k, l) -> l.forEach(v -> mapTo.put(v, k)));

但请注意,即使使用非常大的输入映射,此任务也不会从并行处理中受益。只有当流管道中存在可以从 SMP 中受益的额外计算密集型任务时,才有机会从并行流中受益。因此,简洁、顺序的集合 API 解决方案可能更可取。


答案 2

应按如下方式使用:flatMap

entrySet.stream()
        .flatMap(e -> e.getValue().stream()
                       .map(s -> new SimpleImmutableEntry(e.getKey(), s)));

SimpleImmutableEntry是 中的嵌套类。AbstractMap


推荐