Home > database >  How could I make a stream pipeline more variable in Java?
How could I make a stream pipeline more variable in Java?

Time:08-20

I wrote a stream pipeline:

private void calcMin(Clazz clazz) {
    OptionalInt min = listOfObjects.stream().filter(y -> (y.getName()
                    .matches(clazz.getFilter())))
                    .map(y -> (y.getUserNumber()))
                    .mapToInt(Integer::intValue)
                    .min();
    list.add(min.getAsInt());
}

This pipeline gives me the lowest UserNumber.

So far, so good.

But I also need the greatest UserNumber. And I also need the lowest GroupNumber. And also the greatest GroupNumber.

I could write:

private void calcMax(Clazz clazz) {
    OptionalInt max = listOfObjects.stream().filter(y -> (y.getName()
                    .matches(clazz.getFilter())))
                    .map(y -> (y.getUserNumber()))
                    .mapToInt(Integer::intValue)
                    .max();
    list.add(max.getAsInt());
}

And I could also write the same for .map(y -> (y.getGroupNumber())).

This will work, but it is very redudant.

Is there a way to do it more variable?

CodePudding user response:

There are two differences in the examples: the map() operation, and the terminal operation (min() and max()). So, to reuse the rest of the pipeline, you'll want to parameterize these.

I will warn you up front, however, that if you call this parameterized method directly from many places, your code will be harder to read. Comprehension of the caller's code will be easier if you keep a helper function—with a meaningful name—that delegates to the generic method. Obviously, there is a balance here. If you wanted to add additional functional parameters, the number of helper methods would grow rapidly and become cumbersome. And if you only call each helper from one place, maybe using the underlying function directly won't add too much clutter.

You don't show the type of elements in the stream. I'm using the name MyClass in this example as a placeholder.

    private static OptionalInt extremum(
        Collection<? extends MyClass> input,
        Clazz clazz,
        ToIntFunction<? super MyClass> valExtractor,
        Function<IntStream, OptionalInt> terminalOp) {

        IntStream matches = input.stream()
            .filter(y -> y.getName().matches(clazz.getFilter()))
            .mapToInt(valExtractor);
        return terminalOp.apply(matches);
    }

    private OptionalInt calcMinUserNumber(Clazz clazz) {
        return extremum(listOfObjects, clazz, MyClass::getUserNumber, IntStream::min);
    }

    private OptionalInt calcMaxUserNumber(Clazz clazz) {
        return extremum(listOfObjects, clazz, MyClass::getUserNumber, IntStream::max);
    }

    private OptionalInt calcMinGroupNumber(Clazz clazz) {
        return extremum(listOfObjects, clazz, MyClass::getGroupNumber, IntStream::min);
    }

    private OptionalInt calcMaxGroupNumber(Clazz clazz) {
        return extremum(listOfObjects, clazz, MyClass::getGroupNumber, IntStream::max);
    }

    ...

And here's a usage example:

calcMaxGroupNumber(clazz).ifPresent(list::add);

CodePudding user response:

The solution may reduce redundancy but it removes readability from the code.

 IntStream maxi = listOfObjects.stream().filter(y -> (y.getName()
                .matches(clazz.getFilter())))
                .map(y -> (y.getUserNumber()))
                .mapToInt(Integer::intValue);
    System.out.println(applier(() -> maxi, IntStream::max));
    //System.out.println(applier(() -> maxi, IntStream::min));

...

public static OptionalInt applier(Supplier<IntStream> supplier, Function<IntStream, OptionalInt> predicate) {
    return predicate.apply(supplier.get());
}
  • Related