【问题标题】:What is the difference between using a Predicate or a function as a Java stream filter?使用 Predicate 或函数作为 Java 流过滤器有什么区别?
【发布时间】:2016-04-22 16:16:15
【问题描述】:

所以假设我在流上使用了一些随机过滤器,最直接的方法是直接输入谓词:

x.stream().filter(e -> e % 2 == 0)

我也可以简单地引用并提前定义谓词:

Predicate<Integer> isEven = e -> e % 2 == 0;
...
x.stream().filter(isEven)

但我也可以使用函数:

private static boolean isEven(Integer integer) {
    return integer % 2 == 0;
}
...
x.stream().filter(MyClass::isEven)

据我所知,谓词当然受到更多限制,而函数可能会有副作用等。但是由于像 Venkat Subramaniam 这样的人使用后一种解决方案,我真的想知道:这里的主要区别是什么?

【问题讨论】:

    标签: java-8 java-stream predicate


    【解决方案1】:

    不!与方法引用相比,谓词并没有真正的限制!其实这些都是一样的!

    看看filter()函数签名: filter(Predicate&lt;? super T&gt; predicate)

    让我们看看你的例子:

    x.stream().filter(e -&gt; e % 2 == 0)

    Predicate<Integer> isEven = e -> e % 2 == 0;
    ...
    x.stream().filter(isEven)
    

    第一个只是后者的内联版本。

    private static boolean isEven(Integer integer) {
    return integer % 2 == 0;
    }
    ...
    x.stream().filter(MyClass::isEven)
    

    在这里您可以看到Method References 的实际应用。 MR 只是一种语法糖,允许您基于现有函数定义 Lambda 表达式。

    最终,所有这些表达式都成为 Predicate 功能接口的相同实现。

    此外,您还可以使用右侧的块语法在 Lambda 表达式中执行副作用,但通常不建议这样做:

    e -> {
        //side effects here
        return e % 2 == 0;
    }
    

    【讨论】:

    • 我明白了,如果没有 MR,我们只能定义在可重用性方面确实受到限制的谓词。所以这就引出了一个问题,什么是更干净的写作方式?使用 MR、谓词还是实际语句本身?
    • @AdHominem 这取决于您的上下文。如果你真的不打算重用你的谓词,那就坚持一个简单的 lambda 表达式,但如果你要继续重用它,我建议在一些 utils 类中创建一个静态 Predicate 实例,而不是创建一个方法本身
    【解决方案2】:

    从构建可重用谓词库的角度来看,返回布尔值的函数库比静态最终谓词实例库更通用的谓词集合。为什么?

    考虑一个包含以下内容的库:

    public static boolean isEven(int i) { return i -> i % 2 == 0; }
    

    对比

    public static final Predicate<Integer> IS_EVEN = i -> i % 2 == 0; 
    
    • 如果库函数命名得当,它们的可读性会更好。在长时间缺席后重新访问代码时,扫描filter(MyLib::isEven) 比扫描filter(i -&gt; i % 2 == 0)filter(MyLib::isEven) 更容易告诉您正在调用的确切内容,而filter(MyLib.IS_EVEN) 不会
    • 如果您只是想调用您的库函数而不是将其用作谓词,那么它更具可读性。 MyLib.isEven(i) 扫描效果优于 MyLib.IS_EVEN.test(i)
    • 如果您需要使用IntPredicate 而不是Predicate&lt;Integer&gt; Guava Predicate&lt;Integer&gt;、Apache Collections4 Predicate&lt;Integer&gt; 等,您只需继续使用库函数MyLib::isEven。对于 static final Predicate&lt;Integer&gt; 实例,您必须通过 MyLib.IS_EVEN::test 进行转换(无论如何您最终都会使用方法引用)

    同样的推理适用于所有函数类型。编写函数。它们可以通过简单的方法引用应用于与签名匹配的任何功能类型。

    【讨论】:

      【解决方案3】:

      两者都将返回过滤后的流。使用谓词(返回 true 或 false 的函数)更具可读性......这是上面使用 Function 的简写。

        public class Demo {
      
          public static Function<Integer, Predicate<Integer>> isGreaterThanNumberIamAboutToApply = pivot -> number -> number % pivot == 0;
      
          public static void main(String[] args) {
      
      
              List<Integer> myInts = Arrays.asList(new Integer[]{1, 2, 3, 5, 6, 7, 7})
                      .stream()
                      .filter(isGreaterThanNumberIamAboutToApply
                              .apply(3)).collect(Collectors.toList());
      
              myInts.forEach(x -> System.out.printf("%s\n", x));
      
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 2015-04-18
        • 1970-01-01
        • 2014-05-14
        • 2019-05-01
        • 2019-02-11
        • 2016-10-27
        • 1970-01-01
        • 2010-11-12
        • 1970-01-01
        相关资源
        最近更新 更多