【问题标题】:Java 8 lambda predicate chaining?Java 8 lambda 谓词链接?
【发布时间】:2014-06-24 21:22:14
【问题描述】:

我无法编译它,甚至可以链接谓词 lambdas 吗?

Arrays.asList("1","2","3").stream().filter( (e -> e=="1" ).or(e-> e=="2") ).count();

或者唯一的方法是显式创建一个谓词,然后像这样组合:

Predicate<String> isOne= e -> e=="1";
Arrays.asList("1","2","3").stream().filter( isOne.or(e -> e=="2") ).count();

或者是否有更“功能优雅”的方式来实现相同的目标?

【问题讨论】:

  • 你能举个更好的例子吗?因为在给定的示例中,e -&gt; e == "1" || e == "2" 比连接两个谓词更简单。
  • 我认为or 方法适用于您已经拥有一个谓词并且希望将其与其他谓词链接的情况。如果你同时写两个谓词,你可以直接将它们组合成一个,不需要or方法。
  • 另外,正如我在回答中强调的那样,使用 equals() 比较字符串,不要使用 == (除非您确定所有被比较的字符串都被保留了---这是不是绝大多数情况下)。
  • @nosid,实际上,我认为我无法提供无法使用 || 解决的示例或 && 因为 Predicate 必须返回布尔值。我只是把自己锁定在寻找一个实用的解决方案上。
  • @ChrisJester-Young 是的,我知道使用equals(),而且我特别没有在这段代码中使用它,所以我有疑问的代码部分清晰可见。

标签: java lambda java-8


【解决方案1】:

可以使用:

((Predicate<String>) e -> e.equals("1")).or(e -> e.equals("2"))

但它不是很优雅。如果您在线指定条件,只需使用一个 lambda:

e -> e.equals("1") || e.equals("2")

【讨论】:

  • Lambda 表达式没有内在类型;它们被转换为它们的目标类型,它必须是一个功能接口。这里的问题是它不知道第一个 lambda 表达式使用什么目标类型。这级联;它不知道要调用什么or() 方法。此答案中的示例显示了为第一个带有强制转换的 lambda 提供目标类型,这是您重新注入所需类型信息的选项之一。
  • @BrianGoetz +1 完全是这样,很高兴看到 JSR 335 规范负责人本人的评论。 :-D 关于哪张纸条,也许你知道this question 的答案? :-)
  • @ChrisJester-Young 谢谢!第二部分显然是最干净的解决方案,我只是将自己锁定在寻找结合 lambdas 的解决方案上,而错过了显而易见的解决方案。你的答案的第一部分正是我想要做的。
  • @BrianGoetz 这正是我需要的缺失的理论部分 :)... 并且认为我不再通过“Java 并发实践”来玩 lambdas 和流 :)
【解决方案2】:

我正在研究类似的谓词链接问题并提出以下问题

public static <T> Predicate<T> chain (Function<T,Predicate<T>> mapFn, T[]args) {
    return Arrays.asList(args)
        .stream()
        .map(x->mapFn.apply(x))
        .reduce(p->false, Predicate::or);
}

chain 的第一个参数是一个 lambda(函数),它返回一个 lambda(谓词),所以它需要几个箭头

public static void yourExample() {
    String[] filterVals = { "1", "2" };

    Arrays.asList("1","2","3")
        .stream()
        .filter(chain(x-> (y-> y.equals(x)), filterVals))
        .count();
}

为了比较,这是我试图实现的目标......

public static void myExample() {
    String[] suffixes = { ".png", ".bmp" };
    Predicate<String> p = chain (x-> y-> y.endsWith(x), suffixes);
    File[] graphics = new File("D:/TEMP").listFiles((dir,name)->p.test(name));
    Arrays.asList(graphics).forEach(System.out::println);
}

【讨论】:

  • 这是最好的答案,我将您的方法调整为 BiFunction,例如 BiFunction,Function>, Predicate> chainFilter = (args, f)->args.stream().map(s->f.apply(s)) .reduce(pr->false, Predicate::or);
【解决方案3】:

你可以用魔术方法$:

<T> Predicate<T> $(Predicate<T> p) {
     return p;
}

Arrays.asList("1", "2", "3").stream().filter( $(e -> e=="1").or(e -> e=="2") ).count();

【讨论】:

  • 为什么要为此创建一种新方法呢?顺便说一下,给这个函数命名 $ 并没有什么特别的意义。无论如何,这也有效。
  • 顺便说一下,这个函数的名字$没有特殊含义。是的,有..如果你想让你的代码不可读......
  • 我把这个方法命名为“either”,所以它的用法是这样的:either(e -&gt; e=="1").or(e -&gt; e=="2")
  • @bowmore,太糟糕了..您的 either 方法也有 and 方法
  • @KitKarson 确实如此,但到目前为止这并没有困扰我,因为我倾向于在流本身上使用额外的过滤器调用来链接和。如果它真的困扰你,你可以在Predicate 周围返回一个包装类来隐藏and()
【解决方案4】:

这只是对@Chris Jester-Young 答案的一个小补充。使用方法引用可以使表达式更短:

((Predicate<String>) "1"::equals).or("2"::equals)

【讨论】:

    【解决方案5】:
    Arrays.asList("1","2","3").stream().filter( Arrays.asList("1", "2")::contains).count();
    

    是的方法“要么”是个好主意

        public static void main(String[] args) {
            long count = Arrays.asList("1","2","3").stream().filter(either("1"::equals).or("2"::equals)).count();
            System.out.println(count);
        }
    
        private static <T> Predicate<T> either(Predicate<T> predicate) {
            return predicate;
        }
    

    或者你可以使用import static java.util.function.Predicate.isEqual; 并写isEqual("1").or(isEqual("2"))

    【讨论】:

      【解决方案6】:
      Predicate<String> pred1 =  "1"::equals;
      Predicate<String> pred2 =  "2"::equals;
      
      public void tester(){
            Arrays.asList("1","2","3").stream().filter(pred1.or(pred2)).count();
      }
      

      您可以移动单独的条件,以便以其他方式重新组合它们。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-10-21
        • 1970-01-01
        • 1970-01-01
        • 2015-01-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多