展平地图<整数,列表<string =“”>&gt;使用stream和lambda

时间:2016-11-08 10:49:33

标签: java lambda java-8 java-stream

我想展平一个Map,它将Integer键与String列表相关联,而不会丢失键映射。 我很好奇,好像使用streamlambda这样做是有用和有用的。

我们从这样的事情开始:

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

我们假设mapFrom在某处填充,如下所示:

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

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

现在,我希望&#34;展开&#34;它得到第二张地图:

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来实现此目标?
  • 这样做是否有用(性能,可读性)?

4 个答案:

答案 0 :(得分:17)

您需要使用flatMap将值展平为新流,但由于您仍需要原始密钥来收集Map,因此您必须映射到保存密钥的临时对象和价值,例如

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

这与toMap在静默覆盖重复密钥方面不同,而没有合并函数的toMap将抛出异常(如果存在重复密钥)。基本上,这个自定义收集器是

的并行功能变体
Map<String, Integer> mapTo = new HashMap<>();
mapFrom.forEach((k, l) -> l.forEach(v -> mapTo.put(v, k)));

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

答案 1 :(得分:7)

您应该使用flatMap,如下所示:

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

SimpleImmutableEntryAbstractMap中的嵌套类。

答案 2 :(得分:3)

希望这会以最简单的方式完成。 :))

mapFrom.forEach((key, values) -> values.forEach(value -> mapTo.put(value, key)));

答案 3 :(得分:2)

这应该有效。请注意,您从列表中丢失了一些密钥。

Map<Integer, List<String>> mapFrom = new HashMap<>();
Map<String, Integer> mapTo = mapFrom.entrySet().stream()
        .flatMap(integerListEntry -> integerListEntry.getValue()
                .stream()
                .map(listItem -> new AbstractMap.SimpleEntry<>(listItem, integerListEntry.getKey())))
        .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));