【问题标题】:Java 8 streams - How to extract all objects inside a map of maps to a new map?Java 8流 - 如何将地图地图中的所有对象提取到新地图?
【发布时间】:2018-08-27 10:53:42
【问题描述】:

我有一张地图

siteId -> (AppName -> App) 

我想迭代内部地图中的所有应用程序并创建一个新地图

(appId -> App)

我是在没有流的情况下做的

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

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

如何使用流来做到这一点?

【问题讨论】:

  • 提示:转换嵌套循环,一般需要flatMap

标签: java java-8 java-stream


【解决方案1】:

这样的事情呢?

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

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

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

之后我们终于可以收集到一个新的Map&lt;AppId,App&gt;

【讨论】:

  • nitpick - toMap 收集器的这种重载不能保证返回的 Map 的类型、可变性、可序列化性或线程安全性。尽管目前它正在返回HashMap,但它可能会返回不同类型的地图。假设 OP 为生产代码执行此操作并关心返回的映射类型,最好明确指定您想要一个 HashMap 实例,而不仅仅是为了保证返回的类型。
  • 还向用户记录返回的地图类型。
  • 只是想,我会提到这一点,因为 OP 在他们的代码示例 sn-p 中正在做Map&lt;String, App&gt; result = new HashMap&lt;&gt;();
  • .map(Map::values).flatMap(Collection::stream)
【解决方案2】:

与@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&lt;Collection&lt;App&gt;&gt;,然后flatMap 将所有嵌套的Stream&lt;Collection&lt;App&gt;&gt; 折叠为@ 987654327@,最后toMap收集器将返回一个Collector,它将元素累积到一个Map中,其键是App::getToken的返回值,值是Function.identity()的返回值。

上面的函数(left, right) -&gt; { throw new RuntimeException("duplicate key");}是merge函数,用来解决同一个key关联的值之间的冲突。在这种特殊情况下,您不需要它,但它只是在那里,因此我们可以使用 toMap 收集器的重载,然后允许我们指定我们特别想要一个 HashMap 实例。

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

注意 - 如果您不希望重复键,那么抛出异常(如上所示)是可行的方法,因为它表明存在问题而不是执行其他操作。但是,如果您想在重复键的情况下返回一个值,那么您可以根据需要将合并函数简单地更改为 (left, right) -&gt; left(left, right) -&gt; right

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-01
    • 1970-01-01
    • 2021-03-17
    相关资源
    最近更新 更多