Umar
Umar

Reputation: 1072

Java 8 Applying stream filter based on a condition

In Java 8, is there a way to apply the filter on a stream based on a condition,

example

I have this stream

if (isAccessDisplayEnabled) {
     src = (List < Source > ) sourceMeta.getAllSources.parallelStream()
         .filter(k - > isAccessDisplayEnabled((Source) k))
         .filter(k - > containsAll((Source) k, substrings, searchString))
         .collect(Collectors.toList());
 } else {
     src = (List < Source > ) sourceMeta.getAllSources.parallelStream()
         .filter(k - > containsAll((Source) k, substrings, searchString))
         .collect(Collectors.toList());
 }

I am adding the filter

.filter(k - > isAccessDisplayEnabled((Source) k)))

on the stream based on the if-else condition. Is there a way to avoid that if-else, since if there are more filters coming up,then it will be hard to maintain.

Please let me know

Upvotes: 43

Views: 96632

Answers (3)

Daniel Alder
Daniel Alder

Reputation: 5372

A totally different approach compared to the other anwsers: You can split the fluent syntax of java stream to different parts, and do whatever you need to construct you own perfect pipeline. You can save any part of the stream definition in variables, it is not executed before you call the collect method.

Unverified code (you'll have to fix the type declaration):

Stream<whatever> unfinishedStream = sourceMeta.getAllSources.parallelStream();
if (isAccessDisplayEnabled) {
    unfinishedStream = unfinishedStream.filter(k - > isAccessDisplayEnabled((Source) k));
}
src = (List < Source > ) unfinishedStream 
     .filter(k - > containsAll((Source) k, substrings, searchString))
     .collect(Collectors.toList());

At first, this looks like the most optimal solution. But I heard that java does quite extreme compile-time stream optimization, by transforming them to simple loops. If you start constructing your pipeline dynamically as shown above, Java will likely fall back to simply executing what you defined, with a lot of method calls and internal structure. But I didn't do any benchmarks.

We use this kind of programming a lot for dynamic SQL statements with configurable filters, but there's no such compile-time optimization.

Upvotes: 2

Holger
Holger

Reputation: 298113

One way to do it is

Stream<Source> stream = sourceMeta.getAllSources.parallelStream().map(x -> (Source)x);
if(isAccessDisplayEnabled) stream = stream.filter(s -> isAccessDisplayEnabled(s));
src = stream.filter(s - > containsAll(s, substrings, searchString))
            .collect(Collectors.toList());

another

 src = sourceMeta.getAllSources.parallelStream().map(x -> (Source)x)
     .filter(isAccessDisplayEnabled? s - > isAccessDisplayEnabled(s): s -> true)
     .filter(s - > containsAll(s, substrings, searchString))
     .collect(Collectors.toList());

In either case, note how performing one type cast at the beginning simplifies the entire stream pipline.

Both solutions avoid re-evaluating isAccessDisplayEnabled for every stream element, however, the second relies on the JVM’s capability of inlining s -> true when this code turns out to be performance critical.

Upvotes: 53

jchitel
jchitel

Reputation: 3169

Your condition has the same name as your method. I'm going to assume you meant for those to be different, so let's say it was this:

if (someCondition) {
    src = (List < Source > ) sourceMeta.getAllSources.parallelStream()
        .filter(k - > isAccessDisplayEnabled((Source) k))
        .filter(k - > containsAll((Source) k, substrings, searchString))
        .collect(Collectors.toList());
} else {
    src = (List < Source > ) sourceMeta.getAllSources.parallelStream()
        .filter(k - > containsAll((Source) k, substrings, searchString))
        .collect(Collectors.toList());
}

If you want to remove the if/else, you can instead perform the check in the first filter:

src = (List < Source > ) sourceMeta.getAllSources.parallelStream()
    .filter(k - > !someCondition || isAccessDisplayEnabled((Source) k))
    .filter(k - > containsAll((Source) k, substrings, searchString))
    .collect(Collectors.toList());

In the else case, you take everything and remove the isAccessDisplayEnabled() method call, so the condition is effectively "if someCondition is false or isAccessDisplayEnabled(k)". If someCondition comes out false, then the isAccessDisplayEnabled() check is skipped.

Upvotes: 5

Related Questions