Home > Enterprise >  Implement conditional reduction of two lists into a single list containing two elements using Stream
Implement conditional reduction of two lists into a single list containing two elements using Stream

Time:06-27

I'm comparing two lists based on the following conditions:

  1. If list1 has higher value, 1 to the first element in the result.

  2. If list2 has higher value, 1 to the second element in the result.

  3. If values are the same, skip it.

I have implemented this solution in Java 7

int min = a.size() > b.size() ? b.size() : a.size();
List<Integer> result = Arrays.asList(0, 0);
for(int i =0;  i < min; i  ) {
    if(a.get(i) > b.get(i)) 
        result.set(0, result.get(0)   1);
    else if(a.get(i) < b.get(i))
        result.set(1, result.get(1)   1);
}

return result;

How could I do the same with Java 8 streams?

I came up with two streams and merging the results of both. Because I am not sure how to do both the conditions in single streams.

CodePudding user response:

Using range() method of the IntStream we can create a stream of indices that are valid for these lists.

To filter out indices for which values are not equal, we can apply filter() operation.

Accumulating the result in a list comprised of two elements will require a bit more effort. For that we can use collect(supplier,accumulator,combiner):

  • supplier - a function that returns a new mutable container. Here we need to provide a list that should hold the results of reduction;
  • accumulator - is a function defines the logic on how to add stream elements into the container;
  • combiner - a function that defines how to merge the two containers with partial results obtained while executing the stream in parallel.
List<Integer> a = List.of(1, 2, 5);
List<Integer> b = List.of(1, 2, 8);
    
List<Integer> result = IntStream.range(0, Math.min(a.size(), b.size()))
    .filter(i -> a.get(i) != b.get(i)) // filter out non-equal elements
    .collect(
        () -> Arrays.asList(0, 0), // supplier - provides a container which contain the results of reduction
        (list, i) -> {             // combiner - difines the logic on the container should be updated
            if (a.get(i) > b.get(i)) list.set(0, list.get(0)   1);
            else list.set(1, list.get(1)   1);
        },
        (left, right) -> {         // accumulator - provides the logic for merging the two containers while executing in parallel
            left.set(0, left.get(0)   right.get(0));
            left.set(1, left.get(1)   right.get(1));
        }
    );

System.out.println(result);

Output:

[0, 1]

In case you doubt where it's a correct way to approach this problem with streams, have a look at the API documentation, paragraph Mutable reduction.

And even if after getting familiar with the reference above, you might think that it can be done without collect(). For instance, why don't we simply apply filter().forEach() to make the code leaner? Then have a look at another part of the API documentation, which is dedicated to Side-effects.

  • Related