Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 - Filter with BiPredicate

I have a Stream of Integer and I would like to find the two numbers whose sum is equals to another number. So I came up with the following solution:

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

But the filter method does not receive a BiPredicate. Why not? What is an alternative for that?

like image 648
user2919910 Avatar asked Mar 15 '16 19:03

user2919910


2 Answers

You can still work with Bipredicate. The argument that the filter-method needs is a Predicate, so here is an example of how to use this BiPredicate:

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

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

In this example flavoursPrices must be a List.

The lambda that we are using:

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

Is replacing the anonymous inner class declaration for creating a new Predicate out of the BiPredicate:

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

So to filter the stream, we are creating a new Predicate for every element coming from the stream and than we use this Predicate as argument to use it's test-method. The big advantage of this is, that we don't have to create enormous amounts of Predicates in advance, but we can pass every element in the lambda function and get it's attributes.

like image 139
Matt Avatar answered Oct 20 '22 20:10

Matt


Because filter does not work that way, it only filters one collection at a time. For your task, you need something like Python's itertools.product; this can be implemented using flatMap. In Scala, this looks as short as:

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

If you want to do it in (native) Java, something like this comes out:

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);

    }
}

... which prints [[1, 4], [2, 3], [3, 2], [4, 1]]. But I wouldn't use that in reality ;) At least, a tuple class would be nice to have.

It is worth noting that flatMap one of the most powerful functions there is; almost all stream transformations (filter, map, cartesian product, flatten) can be implemented using it. And rest (like zip) is almost always a fold/reduce (or, rarely, an unfold).

like image 38
phipsgabler Avatar answered Oct 20 '22 20:10

phipsgabler