Java 8流 - 如何将地图中的所有对象提取到新地图?

时间:2018-03-18 22:30:19

标签: java java-8 java-stream

我有地图地图

siteId -> (AppName -> App) 

我想迭代内部地图中的所有应用并创建

的新地图
(appId -> App)

我没有流

Map<String, App> result = new HashMap<>();

siteIdToAppNameToAppMap.forEach((siteId, map) ->
   map.forEach((appName, app) ->
      result.put(app.token, app)
   )
);

我如何使用流?

2 个答案:

答案 0 :(得分:4)

这样的事情怎么样?

siteIdToAppNameToAppMap.values()
   .stream()
   .flatMap(m -> m.values().stream())
   .collect(
        Collectors.toMap(App::getToken, Function.identity())
   );

我们需要使用Stream#flatMap从嵌套地图中提取App。所以stream().values()会给我们Stream<Map<AppName,App>>现在我们需要使用flatMap将其转换为Stream<App>

Stream<Map<AppName,App>> -> flatMap ->  Stream<App>

之后我们终于可以收集到新的Map<AppId,App>

答案 1 :(得分:3)

与@Anton Balaniuc的回答略有不同。

Map<String, App> resultSet = 
           siteIdToAppNameToAppMap.values()
                                  .stream()
                                  .map(Map::values)
                                  .flatMap(Collection::stream)
                                  .collect(Collectors.toMap(App::getToken, 
                                            Function.identity(), (left, right) -> {  
                                       throw new RuntimeException("duplicate key");
                                             },
                                                   HashMap::new));

此解决方案根据siteIdToAppNameToAppMap地图值创建一个流,然后我们对地图值执行map操作,产生Stream<Collection<App>>,然后flatMap将全部折叠嵌套Stream<Collection<App>>Stream<App>,最后toMap收集器将返回一个Collector,它将元素累积到一个Map中,其键的返回值为{{1} }和值是App::getToken的返回值。

上面的函数Function.identity()是合并函数,用于解决与同一个键关联的值之间的冲突。在这种特殊情况下,你不需要它,但它只是那里,所以我们可以使用(left, right) -> { throw new RuntimeException("duplicate key");}收集器的这个重载,然后允许我们指定我们特别想要一个toMap实例。

全部,其他HashMap重载不保证返回的Map的类型,可变性,可序列化或线程安全性。

注意 - 如果你不期望重复的密钥然后抛出异常,如上所示,那就是要走的路,因为它表明存在问题而不是做其他事情。但是,如果在重复键的情况下您想要返回一个值,那么您可以根据需要将合并函数更改为toMap(left, right) -> left