将Map <Integer,List >展平为使用stream和lambda映射

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

我们从这样的事情开始:

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

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

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

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

 Map 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 mapTo = mapFrom.entrySet().stream().collect( HashMap::new, (m,e)->e.getValue().forEach(v->m.put(v, e.getKey())), Map::putAll); 

这与toMap在静默覆盖重复键时不同,而如果存在重复键,没有合并函数的toMap将抛出exception。 基本上,这个自定义收集器是一个并行的变种

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

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

你应该使用flatMap如下:

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

SimpleImmutableEntryAbstractMap的嵌套类。

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

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

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

 Map> mapFrom = new HashMap<>(); Map 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));