【问题标题】:Java Stream Map elements in a list to a numberJava Stream Map 列表中的元素到一个数字
【发布时间】:2020-03-02 17:39:26
【问题描述】:

我将int[] 转换为List<Integer>。手头的任务是将集合中的每个元素映射到 0。所以我想将这个 List 转换为 Set,然后映射到 0。 所以对于一个集合1, 3, 7, 4, 8,它应该看起来像:(1, 0), (3, 0), (7, 0), (4, 0), (8, 0)。为此,我尝试过:

Map<Integer, Integer> map = Arrays.stream(elements)
    .boxed()
    .collect(Collectors.toMap(x -> x, x -> 0));

但是在尝试这个时,我得到了这个错误:

Exception in thread "main" java.lang.IllegalStateException: Duplicate key 0
    at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
    at java.util.HashMap.merge(HashMap.java:1254)
    at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
    at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
    at java.util.stream.IntPipeline$4$1.accept(IntPipeline.java:250)
    at java.util.Spliterators$IntArraySpliterator.forEachRemaining(Spliterators.java:1032)
    at java.util.Spliterator$OfInt.forEachRemaining(Spliterator.java:693)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at DeleteElementsByOccurence.main(DeleteElementsByOccurence.java:18)                  <-- Refers to the Collectors.toMap line

我知道这些东西在 Scala 中非常简单,但我无法弄清楚在 Java 中执行的逻辑。

【问题讨论】:

  • 它实际上是一个集合吗?您的代码暗示了一个元素数组,这不是一个集合。检查数组中是否存在重复元素。
  • 我的错,这是一个列表
  • 如果列表中有重复的数字应该怎么办?是[1,1,2] -&gt; [(1,0), (1,0), (2,0)] 还是[1,1,2] -&gt; [(1,0), (2,0)]
  • 如果你有List,你不应该使用Arrays.stream。你真的有int[]吗?
  • @Sparker0i,然后是.distinct(),或者先制作一个集合,然后流式传输该集合。大多数可行的方法已经有了答案。请注意,.distinct() 在后台也有一个集合,而带有三个 lambda 的 toMap 不会创建一个集合,最终仍然可以按照您的预期工作。

标签: java collections java-8 java-stream


【解决方案1】:

elements 似乎包含重复项。为了解决这个问题,您可以向toMap 提供第三个参数来说明当有重复项时您想要做什么。 (this overload)

.collect(Collectors.toMap(x -> x, x -> 0, (x, y) -> x));

xy 是对应于两个重复键的,您可以决定该键的新值是什么。由于在这种情况下所有值都是 0,所以没有太多选择,是吗?

您实际上不需要先将数组转换为集合,因为第三个参数(merger 函数)会自动处理重复项。

【讨论】:

  • 这个解决方案比.distinct() 稍微好一点,因为它不会在后台分配Set 来跟踪看到的元素,所以对象分配少一点,而且没有集合查找。
  • @M.Prokhorov 你的意思是稍微。更快!= 更好。
  • @Michael,不仅速度更快,而且中间使用的内存更少。涉及Sets 的解决方案更好,因为它在逻辑上更加一致。合并 lambda 在技术上是一个技巧。
【解决方案2】:

如果您的列表包含重复键,则映射将引发异常。您可以使用distinct() 删除重复键。

int[] a = {1,3,7,4,4,8}; // 4 is duplicated

Map<Integer, Integer> map = Arrays.stream(a)
    .boxed()
    .distinct()   // added
    .collect(Collectors.toMap(x -> x, x -> 0));

或者,传递一个合并函数

Map<Integer, Integer> map = Arrays.stream(a)
    .boxed()
    .collect(Collectors.toMap(x -> x, x -> 0, (x, y) -> x));

(同样正确,相同的合并函数:(x, y) -&gt; y(x, y) -&gt; 0

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-02
    • 1970-01-01
    • 2018-10-16
    • 2017-08-24
    • 1970-01-01
    • 1970-01-01
    • 2020-06-28
    • 1970-01-01
    相关资源
    最近更新 更多