Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can intermediate stream operations be encapsulated without breaking the pipeline?

With Java 8 Streams, is it possible to encapsulate and reuse intermediate stream operations in some way that won't break the stream pipeline?

Consider this example from the Java Tutorial on streams:

double average = roster
    .stream()
    .filter(p -> p.getGender() == Person.Sex.MALE)
    .mapToInt(Person::getAge)
    .average()
    .getAsDouble();

Suppose I need to use the filter and mapToInt operations in different places throughout my code. I might want try and to encapsulate that logic so it can be reused, for example:

IntStream maleAges(Stream<Person> stream) {
    return stream
        .filter(p -> p.getGender() == Person.Sex.MALE)
        .mapToInt(Person::getAge)
}

This is nice, but to use it I might have to make a mess of the stream pipeline. For example, if I want the average age of men named Bob:

double averageBob =
    maleAges(roster
        .stream()
        .filter(p -> "Bob".equals(p.getName()))
    )
    .average()
    .getAsDouble();

Is there some better way to do it? I'm thinking of something along these lines:

double averageBob = roster
    .stream()
    .filter(p -> "Bob".equals(p.getName()))
    .apply(this::maleAges) // Doesn't compile
    .average()
    .getAsDouble();
like image 296
Kevin K Avatar asked Feb 11 '16 17:02

Kevin K


1 Answers

One way to do it would be to instead taking a Stream, to take a single Person and emit an IntStream such as :

 final Predicate<Person> isMale = p -> p.getGender() == Person.Sex.MALE;
 final Function<Person, IntStream> maleAges = person -> isMale.test(person)
            ? IntStream.of(person.age)
            : IntStream.empty();
 final double averageT = roster
                .stream()
                .flatMapToInt(maleAges)
                .average()
                .getAsDouble();

This way you can reuse your maleAges function anywhere !

like image 160
Louis F. Avatar answered Nov 15 '22 17:11

Louis F.