【问题标题】:IntStream map function behaviorIntStream 映射函数行为
【发布时间】:2018-07-18 13:17:37
【问题描述】:

我有一小段代码可以计算给定数组中正值、负值和零值的数量。我可以通过迭代数组并计算正值、负值和零值的数量来使用 Java 7 轻松完成此操作。但是对于 Java 8 流,在 map 函数中编写条件是否正确?目前此代码仅迭代一次并跳过数组的其余值。

static void calculatePositiveNegativeAndZero(int[] arr) {
    AtomicInteger positive = new AtomicInteger(0);
    AtomicInteger zero = new AtomicInteger(0);
    AtomicInteger negative = new AtomicInteger(0);
    int len = arr.length;
    Arrays.stream(arr)
            .parallel()
            .map(i -> i > 0 ? positive.getAndIncrement() : (i == 0 ? zero.getAndIncrement() : negative.getAndIncrement()));

    System.out.println(positive.doubleValue()/len);
    System.out.println(negative.doubleValue()/len);
    System.out.println(zero.doubleValue()/len);
}

【问题讨论】:

标签: java arrays java-8 java-stream


【解决方案1】:

您的流管道中没有终端操作,因此根本不应该执行它。你可以用forEach代替map

Arrays.stream(arr)
        .parallel()
        .forEach(i -> {
             int k = i > 0 ? positive.getAndIncrement() : (i == 0 ? zero.getAndIncrement() : negative.getAndIncrement());
        });

【讨论】:

  • 感谢您指出流中没有终端操作。使用 stream.forEach() 时,它说“lambda 表达式中的返回类型错误。int 无法转换为 void”
  • @FaysalMehmood 很奇怪。我确信它会忽略三元条件表达式返回的int 值。无论如何,我更新了代码以使其通过编译,虽然现在有点难看。
  • 三元表达式不是Expression Statement,因此不能用作消费者。但是你可以把它变成一个方法调用,同时去掉重复的代码:.forEach(i -> (i > 0? positive: i == 0? zero: negative).getAndIncrement())。由于方法调用是一个表达式语句,它被允许作为消费者(然后,确实删除了结果 int 值)。
【解决方案2】:

不用AtomicXXX 也可以做同样的事情,但方式有点不同:

Map<Integer, Long> map = Arrays.stream(arr)
            .boxed()
            .collect(Collectors.groupingBy(
                    Integer::signum,
                    Collectors.counting()));

System.out.println("positive = " + map.get(1) + " negative = " + map.get(-1) + " zero = " + map.get(0));

【讨论】:

  • 感谢@Eugene 提供另一种方法。
  • 了解你的图书馆。当您使用.collect(Collectors.groupingBy(Integer::signum, Collectors.counting()));时,此解决方案开始大放异彩...
  • Arrays.stream(arr).mapToObj(Integer::signum).collect(Collectors.groupingBy( Function.identity(), Collectors.counting()));,这样可以减少装箱开销,因为它最多创建三个Integer 实例(-101 的规范表示)。
  • @Holger 好吧...花了一段时间才明白Integer::signum 是如何实现的,谢谢你的提示,我敢打赌我会在一个月内忘记这个。
  • 无所谓,Integer::signum是怎么实现的,只要调用起来比自己实现简单。这将适用,即使它的实现代码看起来与您的完全一样。除此之外,它也可能被称为 JVM 优化器的内在操作,无论其源代码如何。而.mapToObj(Integer::signum) 减少了装箱开销,因为它的合同总是只返回-101 中的任何一个...
【解决方案3】:

您也可以通过创建一个包装类来处理这个问题。而且您不需要使用 AtomicInteger。使用 Java 10 我通过以下方式解决了这个问题。

var wrapper = new Object(){ int positive = 0, negative = 0, zero = 0; };
Arrays.stream(arr)
            .parallel()
            .forEach(i -> { int tmp = i > 0 ? wrapper.positive++ : (i == 0 ? wrapper.zero++ : wrapper.negative++);});

【讨论】:

  • 感谢@Eran 提出创建一个局部变量来保存三元运算结果的想法。
  • 在这种情况下,您可以将其简化为int[] arr = { -1, -2, -3, 0, 0, 0, 3, 2 }; int[] ans = new int[3]; Arrays.stream(arr).forEach(x -&gt; ans[Integer.signum(x) + 1] += 1); System.out.println(Arrays.toString(ans));
猜你喜欢
  • 1970-01-01
  • 2019-10-25
  • 1970-01-01
  • 1970-01-01
  • 2020-01-05
  • 1970-01-01
  • 2012-06-17
  • 2019-02-14
  • 2012-07-26
相关资源
最近更新 更多