【问题标题】:Java 8 Lambdas - Bitwise AND OperationsJava 8 Lambda - 按位与运算
【发布时间】:2017-02-16 12:11:15
【问题描述】:

我目前正在处理使用 Java 8 中的 Lambda 转换使用按位运算的 for 循环的问题。

给定一组复杂的条目,循环需要遍历所有条目并在它们上调用给定的方法(方法返回布尔值)。然后返回结果。

换句话说,我需要在所有条目上调用该方法并存储结果。 这背后的原因是每个条目独立执行复杂的操作,必须执行。最终结果是结果的组合。

代码sn-p:

boolean areAllSuccessful = true;
for (SomeEntry entry : entries) {
     areAllSuccessful = areAllSuccessful & entry.doComplexAction(); // keep calling the method on other entries regardless of the result.
}

return areAllSuccessful;

问题在于 Java 8 中的 Lambda 函数通常会执行短路操作(一旦检测到错误条目,“循环”就会中断并返回错误结果)。

到目前为止,我最好的解决方案是使用 map/filter/count 组合:

return entries
       .stream()
       .map(entry -> entry.doComplexAction())
       .filter(result -> result == false)
       .count() > 0

有没有更聪明/更清洁的方法?

谢谢!

【问题讨论】:

  • 我认为您的用例更适合传统的for 循环而不是流。真的,试图让一切都基于流并不是要走的路。如果它按原样工作,那么就这样吧。
  • 只是评论:isSuccessful() 是一个非常糟糕的名字,因为它具有副作用,会进行大量操作。您应该 1)在问题中明确该方法具有副作用,因此需要在每个条目上调用,以及 2)将该方法重命名为 doComplexActions() 之类的名称。
  • 如果您需要流,请不要使用副作用 XOR 如果您需要副作用,请不要使用流。其他一切都只是自找麻烦。
  • 被调用的方法有副作用,应该写得更显眼(粗体,闪烁,在标题中等等;-))。
  • 不要使用按位运算以避免短路。您可以轻松颠倒顺序 (entry.isSuccessful() && areAllSuccessful) 来强制执行该方法。不要用螺丝刀敲钉子。你只会让所有人感到困惑。此外,不要在行尾放置长注释,因为它会离开屏幕并且开发人员不会阅读它。将它放在它注释的行之前的自己的行中。

标签: java for-loop lambda java-8 bitwise-operators


【解决方案1】:

不应该是这样的吗:

boolean areAllSuccessful = entries.stream()
   .map(entry -> entry.isSuccessful())
   .reduce(Boolean.TRUE, Boolean::logicalAnd);

【讨论】:

  • ……然后等待下一个开发人员看到代码并说“嘿,这看起来像是 allMatch 的完美任务”……
  • @Holger 我也无意中想到了这一点……查看一些在构造函数中有 3 个字段且只有一个是最终字段的代码。我最初的反应是完全删除那个决赛……这里有点像。可能需要代码注释。
  • @Holger Bohemian 刚刚使用allMatch 发布了一个答案:stackoverflow.com/a/42277670
  • 我觉得拥有Boolean::logicalAnd 有点令人不安,但仍然拥有entry -> entry.isSuccessful()。后者不能写SomeEntry::isSuccessful吗?
【解决方案2】:

最简洁、最有效的方法是使用带有allMatch()的方法引用

return entries.stream().allMatch(SomeEntry::isSuccessful);

如果您有 1000 个元素,请考虑改用 parallelStream()

这不会处理每个元素(它在第一个false 返回),所以如果你的isSuccessful() 方法有副作用,这是一个坏名字,你应该重命名它或重构代码来执行副作用在process()(或类似)方法中,并让isSuccessful() 返回结果,如果process() 尚未首先被调用,则抛出IllegalStateException

如果你不重构,一些开发者(包括你)会打电话给isSuccessful(),而没有意识到它“做事”,这可能很糟糕。

【讨论】:

  • 这就是重点,OP不想停留在第一个错误上。这就是他在评论中所说的:实际上我已经隐藏了一些问题。 isSuccessful() 调用背后的操作实际上是复杂的独立系统操作,应该相互独立地执行。最终结果是所有响应的组合
  • 不管旁注(cmets)如何,这对于遇到此问题的其他人(如我)非常有帮助。
  • 我的回答解释了 allMatch() 解决方案,我的 cmets 解释了像 isSuccessful() 这样的名字真的很糟糕。但是你的回答解释了这两者并且更清楚。 +1。
【解决方案3】:

如果你打算使用count(),你真的不需要map()

return !(entries
        .stream()
        .filter(entry -> !entry.isSuccessful())
        .count() > 0);

如果方法isSuccessful()没有副作用,你只需要知道如果所有输入都成功,你可以使用allMatch()

return entries
        .stream()
        .allMatch(entry -> entry.isSuccessful());

这确实是一个短路操作,一旦找到isSuccessful()false 的条目就会返回false,除非必要,否则不会消耗整个流。但是你已经评论了isSuccesful() 的真正意思是“做一些复杂的动作然后然后告诉我他们是否成功”,所以它不适用。

【讨论】:

  • 困扰我的是评论:keep calling the method on other entries regardless of the result; allMatch 将在找到第一个不匹配的元素后立即停止处理。
  • @Eugene 是的,我刚刚注意到了。但这很奇怪,因为在示例中,显然不需要处理所有元素,也不需要按位或。
  • 其实我已经隐藏了一点问题。 isSuccessful() 调用背后的操作实际上是复杂的独立系统操作,应该相互独立地执行。最终结果是所有响应的组合。
  • @xanmcgregor 哦,我明白了。好吧,我的最后一次编辑就这么多:D 我会回到我的第一个答案!
猜你喜欢
  • 2012-09-06
  • 2016-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-28
  • 1970-01-01
  • 2017-09-27
  • 1970-01-01
相关资源
最近更新 更多