用Java链接谓词的首选方法是什么?

时间:2019-09-10 20:11:17

标签: java functional-programming predicate functional-interface

我有一堆谓词,我想将它们与逻辑“和”链接在一起,以便只有在所有单个谓词的评估结果都为true时,最终结果才为true。

如我所见,有两种写方法。我可以像这样将它们链接在一起:

Predicate composedPredicate = 
  predicate1
  .and(predicate2)
  .and(predicate3)
  .and(predicate4)

或者我可以使用这样的更嵌套的方法:

Predicate composedPredicate = 
  predicate1
  .and(predicate2
       .and(predicate3
            .and(predicate4)))

显然,选项1更具可读性,但似乎效率可能略低。我认为选项1大致等于: (((p1 && p2) && p3) && p4)

选项2为: (p1 && (p2 && (p3 && p4)))

在第二种情况下,将对第一个参数p1求值,如果它为假,则整个事件立即短路,您就完成了。在第一个选项中,第一个参数实际上是整个表达式((p1 && p2) && p3),它本身具有(p1 && p2)的第一个参数,而后者又将p1作为其第一个参数。基本上,在实际可以评估p1之前,您需要再进行2个“步骤”。我实际上不知道Java如何实现默认谓词方法,因此如果我在这里错了,请纠正我。

有什么办法可以做到两全其美吗?如果不是,相对于性能提升幅度很小或反之亦然,我是否应该采用更具可读性的方法呢?

1 个答案:

答案 0 :(得分:4)

通常,在任何可感知的边际性能方面,都应考虑可读性。对于大多数谓词,我们希望它们执行得非常快,并且很难注意到这样的组合谓词:早期短路且经常短路,而不是晚期短路或根本不短路。

因此,从您的第一个选项(即更具可读性的选项)开始,即不要嵌套谓词以弄乱操作顺序。

如果由于某种探查器统计信息或基准性能运行而确实发现了性能问题,则可以重新排序操作以获得性能优势。

  1. 您可以选择首先放置最有可能发生短路的谓语,以避免执行不会影响结果的谓语的不必要开销。因为您在它们之间使用and,所以它们更可能返回false
  2. 如果您多次调用这些链接谓词,请查看是否可以找到任何不变的谓词,例如不需要依赖循环迭代索引,而是保存这些结果,因此无需一遍又一遍地重复。
  3. 如果您发现其中一个谓词是计算密集型的,并且在循环中不是不变的,则需要一遍又一遍地执行它,然后将其放置在链中的最后,以查看先前的谓词是否可能会短路-电路,因此根本不需要执行。

但这也不应该影响可读性。您不必像已经完成的那样嵌套谓词。最多您需要重新排序它们,例如

Predicate composedPredicate = 
  predicate4
  .and(predicate2)
  .and(predicate1)
  .and(predicate3)

如果您注意到predicate4很少返回true并且predicate3的计算量很大。