【问题标题】:What is the preferred way to chain predicates in Java?在 Java 中链接谓词的首选方式是什么?
【发布时间】:2020-01-12 14:43:29
【问题描述】:

我有一堆谓词,我想用逻辑“and”将它们链接在一起,这样最终结果只有在所有单独的谓词都评估为真时才为真。

在我看来,有两种方法可以写这个。我可以像这样将它们链接在一起:

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 是如何实现默认谓词方法的,如果我在这里错了,请纠正我。

有什么方法可以两全其美?如果不是,我应该更倾向于使用更具可读性的方法,而不是可能带来非常微弱的性能提升,反之亦然?

【问题讨论】:

    标签: java functional-programming predicate functional-interface


    【解决方案1】:

    通常,任何感知到的边际性能考虑因素都支持可读性。对于大多数谓词,我们希望它们能够非常快速地执行,并且很难注意到这样的组合谓词会提前短路并且经常与那些后期短路或根本不短路的谓词相比。

    所以从你的第一个选项开始,更易读的那个,它不会嵌套谓词来弄乱操作顺序。

    如果您确实发现由于某种分析器统计数据或基准性能运行而存在性能问题,那么您可以重新排序操作以获得性能优势。

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

    但这也不应该影响可读性。您不需要像以前那样嵌套谓词;最多你需要重新排序它们,例如

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

    如果您注意到predicate4 很少返回true 并且predicate3 是计算密集型的。

    【讨论】:

    • 重新排序和您提到的其他方法是我所拥有的正交问题。我担心的是,即使假设第一个谓词会缩短 curcuit:可读版本仍然可能产生比嵌套版本更高的堆栈。
    • 只有调用谓词(方法)时,执行堆栈才会增长,我不会担心4个方法的执行堆栈。
    • +1 有利于提高可读性而不是边际性能提升。代码是人的,人的。它只是偶然被机器读取。 Knuth 风格。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-16
    • 2020-09-17
    • 2015-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-24
    相关资源
    最近更新 更多