【问题标题】:Java 8 - Filter with BiPredicateJava 8 - 使用 BiPredicate 进行过滤
【发布时间】:2016-07-01 10:49:21
【问题描述】:

我有一个整数流,我想找到总和等于另一个数字的两个数字。所以我想出了以下解决方案:

BiPredicate<Integer, Integer> p = (price1, price2) -> price1.intValue() + price2.intValue() == moneyQty;
flavoursPrices.filter(p);

但是过滤器方法没有收到 BiPredicate。为什么不?有什么替代方法?

【问题讨论】:

  • 流一次处理一个元素。过滤器检查每个元素的条件,并决定是否通过它。当您只有一个对象要决定时,您不能使用双参数谓词。
  • 因为 Stream 是 Integer 实例的序列,而不是整数对的序列。

标签: java lambda filter functional-programming predicate


【解决方案1】:

您仍然可以使用 Bipredicate。 filter-method 需要的参数是 Predicate,所以这里是一个如何使用这个 BiPredicate 的例子:

 BiPredicate<Integer, Integer> p = (price1, price2) -> price1.intValue() + price2.intValue() == moneyQty;

flavoursPrices.stream().filter(el->p.test(el.price1,el.price2));

在这个例子中,flavorsPrices 必须是一个列表。

我们正在使用的 lambda:

el->p.test(el.price1,el.price2)

正在替换匿名内部类声明以从 BiPredicate 中创建新的 Predicate:

 Predicate<Integer> arg =new Predicate<Integer>() {
        @Override
        public boolean test(Element el) {
            return p.test(el.price1,el.price2);
        }
    };

所以为了过滤流,我们为来自流的每个元素创建一个新的谓词,然后我们使用这个谓词作为参数来使用它的测试方法。这样做的最大好处是,我们不必预先创建大量的 Predicates,但我们可以传递 lambda 函数中的每个元素并获取它的属性。

【讨论】:

    【解决方案2】:

    因为filter 不是这样工作的,它一次只过滤一个集合。对于您的任务,您需要 Python 的 itertools.product 之类的东西;这可以使用flatMap 来实现。在 Scala 中,这看起来很短:

    prices.flatMap(p1 => 
      prices.flatMap(p2 => 
        if (p1 + p2 == 5) List((p1, p2)) else List()))
    

    如果你想用(原生)Java 来做,会出现这样的结果:

    import java.util.*;
    import java.util.stream.*;
    
    public class Main {
    
        public static void main(String[] args) {
    
            List<Integer> prices =  Arrays.asList(1,2,3,4,5,6,7,8,9);
    
            List<List<Integer>> result = prices.stream().flatMap(p1 ->
                prices.stream().flatMap(p2 ->
                    p1 + p2 == 5 ? Stream.of(Arrays.asList(p1, p2)) : Stream.empty()
                )
            ).collect(Collectors.toList());
    
            System.out.println(result);
    
        }
    }
    

    ...打印[[1, 4], [2, 3], [3, 2], [4, 1]]。但我不会在现实中使用它;)至少,拥有一个元组类会很好。

    值得注意的是flatMap是最强大的功能之一;几乎所有的流转换(过滤器、映射、笛卡尔积、展平)都可以使用它来实现。其余部分(如 zip)几乎总是折叠/缩小(或者,很少是展开)。

    【讨论】:

    • 是的。我很好奇,但在 Scala 中看起来要容易得多。感谢您的考试:)
    • @phg,为了便于阅读,您可以将更大的块简化为p1 + p2 == 5 ? Stream.of(Arrays.asList(p1, p2)) : Stream.empty(),从而得到List&lt;List&lt;Integer&gt;&gt;。很好的答案!
    • @YassinHajaj 它变得更好了;同样可以写成for { p1 &lt;- prices; p2 &lt;- prices; if p1 + p2 == 5 } yield (p1, p2)
    【解决方案3】:

    Predicate != BiPredicate

    Stream#filter 接受 Predicate (Object -&gt; boolean) 将一次处理每个元素。

    您可能需要查看StreamEx 库以获取解决方案。

    它具有将流中连续元素配对的方法。

    StreamEx

    【讨论】:

    • @user2919910 作为替代方案,您可以使用 flatMap: prices.flatMap(p1 =&gt; prices.flatMap(p2 =&gt; if (p1 + p2 == 5) List((p1, p2)) else List()))(在 Scala 中,但应该易于翻译)。
    • @phg 那会很有趣,您介意用您的解决方案发布答案​​吗?
    • 我会尝试,但请给我一些时间来挖掘我的 Java 技能;)
    • 所以,there 你有那个可憎的......我总是想知道为什么他们没有在 Stream 类中包含完整的功能集。或者提供元组。
    【解决方案4】:

    如何使用StreamEx library 实施您的解决方案:

    StreamEx.ofPairs(prices, (p1, p2) -> p1 + p2 == 5 ? StreamEx.of(p1, p2) : StreamEx.empty())
                .flatMap(Function.identity())
                .forEach(price -> System.out.println(price));
    

    您可能还想创建自己的类来封装这些对。

    【讨论】:

    • 很好的提示,导师!
    猜你喜欢
    • 1970-01-01
    • 2014-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-02
    • 2017-06-21
    • 2015-05-14
    相关资源
    最近更新 更多