Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to encapsulate Java Streams operations [closed]

One of the drawbacks which I see when using the Java Stream API is that the code seems less readable when compared to its iterative version.

By the way, that is what I think, but I am just giving some context (to avoid the XY problem), what I am going to ask is independent of that, so hopefully this will not be considered an opinion based question.

Encapsulation usually increases code readability (again, just given context) so I am looking for the best way to encapsulate Java Stream operations.

For example, this is a simple series of Stream operations implemented the typical way (no encapsulation):

    void method1() {
        List<Integer> result = IntStream.range(1, 10)

                // Two Stream "intermediate" operations:
                .filter(i -> i % 2 != 0).map(i -> i * 2)

                .boxed().collect(Collectors.toList());

        log.debug("Result: {}", result);
        // Result: [2, 6, 10, 14, 18]
    }

And this is my first and simple attempt to achieve encapsulation, by adding the filterOutEvenNumbersAndMultiplyByTwo method:

    IntStream filterOutEvenNumbersAndMultiplyByTwo(IntStream stream) {
        return stream.filter(i -> i % 2 != 0).map(i -> i * 2);
    }

    void method2() {
        List<Integer> result = filterOutEvenNumbersAndMultiplyByTwo(IntStream.range(1, 10))
                .boxed().collect(Collectors.toList());

        log.debug("Result: {}", result);
        // Result: [2, 6, 10, 14, 18]
    } 

As you can see I have encapsulated two intermediate Stream operations inside a new method, which does not seem to make much sense in this case but it is just an example, there could be more than two Stream operations and also more complicated ones.

The new method increases readability (IMHO) and can be unit tested (that's a fact).
The code above works, but it does not look very nice or clean, ideally, I would like to be able to write something such as:

        List<Integer> result = IntStream.range(1, 10)

                // This obviously does not compile
                .filterOutEvenNumbersAndMultiplyByTwo()

                .boxed().collect(Collectors.toList());

I guess that it could be done but it would require significant effort since I would have to implement the Stream interface...

Is there another way?
Maybe a library exists which I could use?
Maybe my OO mind is trying to force encapsulation on something that's not meant to but I still wonder if it is possible.

like image 387
Víctor Gil Avatar asked Jun 07 '20 15:06

Víctor Gil


Video Answer


1 Answers

filterOutEvenNumbers is simply a predicate. multiplyByTwo is a map operation.

// we can unit test these
IntPredicate filterOutEvenNumbers = i -> i % 2 != 0
IntUnaryOperator multiplyByTwo = i -> i * 2;

You can have N number of Predicates / map operations. We can also switch various predicates / map operations here.

IntStream.range(1, 10)
    .filter(filterOutEvenNumbers)
    .map(multiplyByTwo)
    .boxed()
    .collect(Collectors.toList());
like image 125
RamPrakash Avatar answered Oct 27 '22 08:10

RamPrakash