Java 8 Streams - 分组为单个值[重复]

Posted

技术标签:

【中文标题】Java 8 Streams - 分组为单个值[重复]【英文标题】:Java 8 Streams - Grouping into Single value [duplicate] 【发布时间】:2017-08-25 13:50:44 【问题描述】:

我目前正在使用List<Map<String, Object>>,我正在尝试对地图中的各种键进行分组。 这似乎使用 Java 8 Streams 可以很好地工作:

Map<Object, Map<Object, List<Map<String, Object>>>> collect =
   list
   .stream()
   .collect(Collectors.groupingBy(
       item -> item.get("key1"),
       Collectors.groupingBy(item -> item.get("key2"))
   ));

正如预期的那样,这给了我一个Map&lt;Object, Map&lt;Object, List&lt;Map&lt;String, Object&gt;&gt;&gt;&gt;,它在可能的分组结果大于 1 时效果很好。

例如,我有各种示例,其中正在进行的分组将总是导致最低级别列表中的单个项目。

行列表


  [reference="PersonX", firstname="Person", dob="test", lastname="x"],
  [reference="JohnBartlett", firstname="John", dob="test", lastname="Bartlett"]

按参考分组

当前 - 与 1 Map&lt;String,Object&gt; 分组到一个列表中

[PersonX,  [reference="PersonX", firstname="Person", dob="test", lastname="x"]],
[JohnBartlett,  [reference="JohnBartlett", firstname="John", dob="test", lastname="Bartlett"]]

偏好 - 没有列表,只有一个 Map&lt;String,Object&gt;

[PersonX, [reference="PersonX", firstname="Person", dob="test", lastname="x"]],
[JohnBartlett, [reference="JohnBartlett", firstname="John", dob="test", lastname="Bartlett"]]

流中是否有办法强制这些实例的输出为 Map&lt;Object, Map&lt;Object, Map&lt;String, Object&gt;&gt;&gt; - 所以单个 Map&lt;String,Object&gt; 而不是其中的 List

任何帮助将不胜感激。

【问题讨论】:

这能回答你的问题吗? Java 8 List<V> into Map<K, V> 【参考方案1】:

如果我理解正确,那么对于您确定只有一个项目的情况,您应该替换:

 .collect(Collectors.groupingBy(
   item -> item.get("key1"),
   Collectors.toMap(item -> item.get("key2"), Function.identity())
 ));

您甚至可以提供第三个参数作为BinaryOperator 来合并您的相同条目(以备不时之需)

【讨论】:

如果实际上有多个具有相同键的值会发生什么?我猜这个值会被后来的事件覆盖?对此进行解释,您的答案会更加有力。在决定是否使用这种方式收集到地图时,这可能是一个决定性因素。 @Magnilex 如果实际上有多个具有相同键的值会发生什么,请提供一个简单的示例?通常,toMap 按 Key 工作,因此一旦您达到“第二次”相同的键,没有合并功能,toMap 将失败并出现异常。如果你的意思是别的,请澄清 如果我理解正确,那就是我的意思。举个例子,你有一个实体列表,以它们的 id:s 作为键收集到一个地图。如果存在多个具有相同 id 的实体,上述方法会抛出异常吗? @Magnilex 是的,除非您提供合并函数作为toMap 的第三个参数,否则会抛出带有一些重复错误消息的异常【参考方案2】:

Collectors.toMap() 完全符合您的要求。

Map<Object, Map<String, Object>> collect = maps.stream()
        .collect(Collectors.toMap(p -> p.get("reference"), Function.identity()));

输出:


  PersonX=firstname=Person, reference=PersonX, lastname=x, dob=test,
  JohnBartlett=firstname=John, reference=JohnBartlett, lastname=Bartlett, dob=test

如果您有重复的键,这将抛出 IllegalStateException,这可能正是您想要的,因为您从不期望数据中有重复的记录:

Exception in thread "main" java.lang.IllegalStateException: Duplicate key JohnBartlett (attempted merging values dob=test, lastname=Bartlett, reference=JohnBartlett, firstname=John and dob=test 2, lastname=Bartlett, reference=JohnBartlett, firstname=John)
    at java.base/java.util.stream.Collectors.duplicateKeyException(Collectors.java:133)
    at java.base/java.util.stream.Collectors.lambda$uniqKeysMapAccumulator$1(Collectors.java:180)
    at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
    at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:720)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
    at so.UniqueKeyStreamExample.main(UniqueKeyStreamExample.java:22)

【讨论】:

以上是关于Java 8 Streams - 分组为单个值[重复]的主要内容,如果未能解决你的问题,请参考以下文章

Java 8 Stream 函数将字谜列表分组为列表映射

C# 8中的Async Streams

Java Streams API - 按多个字段分组

Hashmap with Streams in Java 8 Streams 收集 Map 的值

使用 Java-8 Streams API 将字符串列表转换为 Map

Java 8 Stream得到最少