【问题标题】:Simplifying the if-else condition using Java Functional Style Programming使用 Java 函数式编程简化 if-else 条件
【发布时间】:2019-03-18 07:02:06
【问题描述】:

对使用 Java 8 功能简化以下代码块有何建议?

int[] ans = new int[2];
list.forEach(i -> {
    if (i > 0) {
        ans[0] += 1;
    } else if (i < 0) {
        ans[1] += 1;
    }
});

附:不知道我是否应该发布这个here

【问题讨论】:

标签: java optimization java-8


【解决方案1】:

如果您不想计算零,您的代码将尽可能简单。但是,如果您想将零计为正数,则可以将其缩短为这个。

int[] ans = new int[2];
for (int i : list) ans[i < 0 ? 1 : 0] += 1;

【讨论】:

  • @Eugene 没有&lt;&lt;&lt; 也许你的意思是&gt;&gt;&gt; 如果你不知道i 的类型,你可以使用i &gt;&gt;&gt; -1,它适用于intlong .
  • @PeterLawrey 哈!必须去规范以了解这是指定的还是黑客,很好。谢谢你
【解决方案2】:
 ans[0] = (int)list.stream().filter(x -> x < 0).count();
 ans[1] = (int)list.stream().filter(x -> x > 0).count();

但我几乎不认为这是一种简化,您的解决方案已经尽可能简单了。

如果您确实想计算零,这可以简化为:

 list.forEach(x -> ++ans[x >>> 31])

【讨论】:

  • 您实际上是在这里进行了两次迭代工作,不是吗?我同意没有比提供的更简单的解决方案
  • @LoztInSpace 你是绝对正确的,正如所说,这几乎不是一个简化......
  • @Eugene,您不需要第二次迭代。 ans[1] = list.size() - ans[0]
  • @HadiJ 我想过这一点,但在这种情况下,我们不考虑 0 并且 OP 的代码会考虑
  • 是的,我也是这么想的。在 C 语言中,您可以使用 ++ans[i&gt;&gt;31] 之类的东西,但那些讨厌的 0 会妨碍您,
【解决方案3】:

我建议

int[] ans = new int[2];
list.forEach(i -> ans[i >>> 31] += i==0 ? 0 : 1);

其中i &gt;&gt;&gt; 31 丢弃除符号位以外的所有位(即与i&lt;0 ? 1 : 0 相同),第二个条件句柄为零。

我不说,它确实比原版更好。

【讨论】:

  • 你可以用.forEach(i -&gt; ans[i &gt;&gt;&gt; 31] += i&gt;&gt;&gt;31|-i&gt;&gt;&gt;31)完全无分支地完成它
  • @Holger 很好很简单。我想,我可以希望使用cmov 翻译我的条件,但是由于公共子表达式,您的表达式甚至可以更快。
  • @maaartinus 这几乎Integer::signum btw 背后的诀窍,即使没有这种简化,仍然是非常好的答案...1+
【解决方案4】:

为了区分i &gt; 0i &lt; 0这两种情况,我们可以使用Stream.partition

Map<Boolean, List<Integer>> partitioned = list.stream()
  .filter(i -> i != 0)
  .collect(Collectors.partitioningBy(i -> i > 0));
ans[0] = partitioned.get(true).size();
ans[1] = partitioned.get(false).size();

简化了吗?至少它仍然可读且易于理解。

编辑
或者正如@saka1029 建议的那样:

Map<Boolean, Long> partitioned = list.stream()
  .filter(i -> i != 0)
  .collect(Collectors.partitioningBy(i -> i > 0, Collectors.counting()));
ans[0] = partitioned.get(true);
ans[1] = partitioned.get(false);

编辑
还有一个返回所需数组的Stream 解决方案。但我认为这并不简单。所以要比较一下。

int[] ans = list.stream().filter(i -> i != 0).collect(
  () -> new int[2], 
  (arr, i) -> arr[i > 0 ? 0 : 1]++, 
  (l, r) -> {  l[0] += r[0]; l[1] += r[1]; });

【讨论】:

  • 您可以通过.collect(Collectors.partitioningBy(i -&gt; i &gt; 0, Collectors.counting()))收集计数
  • @saka1029 感谢您的建议。我将其包含在我的答案中。
【解决方案5】:

这里也有一种计算零的方法:

int[] ans = new int[3];
list.forEach(i -> ans[Integer.signum(i) + 1] += 1);

ans 数组现在包含 3 个值:索引 0 表示负计数,索引 1 表示零计数,索引 2 表示正计数。

即对于以下输入:[1, 2, 3, 0, -4, -5],输出将是:[2, 1, 3]


一种更实用的方式,不包括零:

Map<Integer, Long> bySignum = list.stream()
    .filter(i -> i != 0)
    .collect(Collectors.groupingBy(Integer::signum, Collectors.counting()));

产生此输出:{-1=2, 1=3}

【讨论】:

  • 好吧,如果你碰巧知道Integer::signum 是如何实现的,i &gt;&gt; 31 | -i &gt;&gt;&gt; 31 - 这真是太聪明了,你可以说ans[i &gt;&gt;&gt; 31] = i &gt;&gt;&gt; 31 | -i &gt;&gt;&gt; 31,正如 Holger 上面所示。对此的解释也很有趣。如果i 是负数,i &gt;&gt;&gt; 31 将给出1(索引),如果i 是正数i &gt;&gt;&gt; 31 将给出0。那就是索引计算。而i &gt;&gt;&gt; 31 | -i &gt;&gt;&gt; 31的部分更有趣!!
  • @Eugene 好吧,是的,我猜这很聪明。我不会为此牺牲代码的可读性,如果我们只是玩得开心也没关系(这相当于玩填字游戏的现代版),它是ans[i &gt;&gt;&gt; 31] += i &gt;&gt;&gt; 31 | -i &gt;&gt;&gt; 31(你忘了+sign)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-10
  • 1970-01-01
相关资源
最近更新 更多