Java 8 Streams: multiple filters vs. complex condition












160















Sometimes you want to filter a Stream with more than one condition:



myList.stream().filter(x -> x.size() > 10).filter(x -> x.isCool()) ...


or you could do the same with a complex condition and a single filter:



myList.stream().filter(x -> x.size() > 10 && x -> x.isCool()) ...


My guess is that the second approach has better performance characteristics, but I don't know it.



The first approach wins in readability, but what is better for the performance?










share|improve this question




















  • 36





    Write whichever code is more readable in the situation. The performance difference is minimal (and highly situational).

    – Brian Goetz
    Jun 5 '14 at 16:50






  • 5





    Forget about nano-optimizations and use highly readable & maintainable code. with streams, one should always use each operation seperately including filters.

    – Diablo
    Feb 27 '18 at 18:44
















160















Sometimes you want to filter a Stream with more than one condition:



myList.stream().filter(x -> x.size() > 10).filter(x -> x.isCool()) ...


or you could do the same with a complex condition and a single filter:



myList.stream().filter(x -> x.size() > 10 && x -> x.isCool()) ...


My guess is that the second approach has better performance characteristics, but I don't know it.



The first approach wins in readability, but what is better for the performance?










share|improve this question




















  • 36





    Write whichever code is more readable in the situation. The performance difference is minimal (and highly situational).

    – Brian Goetz
    Jun 5 '14 at 16:50






  • 5





    Forget about nano-optimizations and use highly readable & maintainable code. with streams, one should always use each operation seperately including filters.

    – Diablo
    Feb 27 '18 at 18:44














160












160








160


26






Sometimes you want to filter a Stream with more than one condition:



myList.stream().filter(x -> x.size() > 10).filter(x -> x.isCool()) ...


or you could do the same with a complex condition and a single filter:



myList.stream().filter(x -> x.size() > 10 && x -> x.isCool()) ...


My guess is that the second approach has better performance characteristics, but I don't know it.



The first approach wins in readability, but what is better for the performance?










share|improve this question
















Sometimes you want to filter a Stream with more than one condition:



myList.stream().filter(x -> x.size() > 10).filter(x -> x.isCool()) ...


or you could do the same with a complex condition and a single filter:



myList.stream().filter(x -> x.size() > 10 && x -> x.isCool()) ...


My guess is that the second approach has better performance characteristics, but I don't know it.



The first approach wins in readability, but what is better for the performance?







java lambda filter java-8 java-stream






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited May 10 '16 at 10:56









Holger

164k23232441




164k23232441










asked Jun 5 '14 at 8:01









deamondeamon

36.9k85242369




36.9k85242369








  • 36





    Write whichever code is more readable in the situation. The performance difference is minimal (and highly situational).

    – Brian Goetz
    Jun 5 '14 at 16:50






  • 5





    Forget about nano-optimizations and use highly readable & maintainable code. with streams, one should always use each operation seperately including filters.

    – Diablo
    Feb 27 '18 at 18:44














  • 36





    Write whichever code is more readable in the situation. The performance difference is minimal (and highly situational).

    – Brian Goetz
    Jun 5 '14 at 16:50






  • 5





    Forget about nano-optimizations and use highly readable & maintainable code. with streams, one should always use each operation seperately including filters.

    – Diablo
    Feb 27 '18 at 18:44








36




36





Write whichever code is more readable in the situation. The performance difference is minimal (and highly situational).

– Brian Goetz
Jun 5 '14 at 16:50





Write whichever code is more readable in the situation. The performance difference is minimal (and highly situational).

– Brian Goetz
Jun 5 '14 at 16:50




5




5





Forget about nano-optimizations and use highly readable & maintainable code. with streams, one should always use each operation seperately including filters.

– Diablo
Feb 27 '18 at 18:44





Forget about nano-optimizations and use highly readable & maintainable code. with streams, one should always use each operation seperately including filters.

– Diablo
Feb 27 '18 at 18:44












3 Answers
3






active

oldest

votes


















111














The code that has to be executed for both alternatives is so similar that you can’t predict a result reliably. The underlying object structure might differ but that’s no challenge to the hotspot optimizer. So it depends on other surrounding conditions which will yield to a faster execution, if there is any difference.



Combining two filter instances creates more objects and hence more delegating code but this can change if you use method references rather than lambda expressions, e.g. replace filter(x -> x.isCool()) by filter(ItemType::isCool). That way you have eliminated the synthetic delegating method created for your lambda expression. So combining two filters using two method references might create the same or lesser delegation code than a single filter invocation using a lambda expression with &&.



But, as said, this kind of overhead will be eliminated by the HotSpot optimizer and is negligible.



In theory, two filters could be easier parallelized than a single filter but that’s only relevant for rather computational intense tasks¹.



So there is no simple answer.



The bottom line is, don’t think about such performance differences below the odor detection threshold. Use what is more readable.





¹…and would require an implementation doing parallel processing of subsequent stages, a road currently not taken by the standard Stream implementation






share|improve this answer





















  • 2





    doesn't the code have to iterate the resulting stream after each filter?

    – jucardi
    Nov 20 '16 at 23:47






  • 9





    @Juan Carlos Diaz: no, streams don’t work that way. Read about “lazy evaluation”; intermediate operations don’t do anything, they only alter the outcome of the terminal operation.

    – Holger
    Nov 21 '16 at 9:24



















20














This test shows that your second option can perform significantly better. Findings first, then the code:



one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=4142, min=29, average=41.420000, max=82}
two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=13315, min=117, average=133.150000, max=153}
one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10320, min=82, average=103.200000, max=127}


now the code:



enum Gender {
FEMALE,
MALE
}

static class User {
Gender gender;
int age;

public User(Gender gender, int age){
this.gender = gender;
this.age = age;
}

public Gender getGender() {
return gender;
}

public void setGender(Gender gender) {
this.gender = gender;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}

static long test1(List<User> users){
long time1 = System.currentTimeMillis();
users.stream()
.filter((u) -> u.getGender() == Gender.FEMALE && u.getAge() % 2 == 0)
.allMatch(u -> true); // least overhead terminal function I can think of
long time2 = System.currentTimeMillis();
return time2 - time1;
}

static long test2(List<User> users){
long time1 = System.currentTimeMillis();
users.stream()
.filter(u -> u.getGender() == Gender.FEMALE)
.filter(u -> u.getAge() % 2 == 0)
.allMatch(u -> true); // least overhead terminal function I can think of
long time2 = System.currentTimeMillis();
return time2 - time1;
}

static long test3(List<User> users){
long time1 = System.currentTimeMillis();
users.stream()
.filter(((Predicate<User>) u -> u.getGender() == Gender.FEMALE).and(u -> u.getAge() % 2 == 0))
.allMatch(u -> true); // least overhead terminal function I can think of
long time2 = System.currentTimeMillis();
return time2 - time1;
}

public static void main(String... args) {
int size = 10000000;
List<User> users =
IntStream.range(0,size)
.mapToObj(i -> i % 2 == 0 ? new User(Gender.MALE, i % 100) : new User(Gender.FEMALE, i % 100))
.collect(Collectors.toCollection(()->new ArrayList<>(size)));
repeat("one filter with predicate of form u -> exp1 && exp2", users, Temp::test1, 100);
repeat("two filters with predicates of form u -> exp1", users, Temp::test2, 100);
repeat("one filter with predicate of form predOne.and(pred2)", users, Temp::test3, 100);
}

private static void repeat(String name, List<User> users, ToLongFunction<List<User>> test, int iterations) {
System.out.println(name + ", list size " + users.size() + ", averaged over " + iterations + " runs: " + IntStream.range(0, iterations)
.mapToLong(i -> test.applyAsLong(users))
.summaryStatistics());
}





share|improve this answer



















  • 2





    Interesting - when I change the order to run test2 BEFORE test1, test1 runs slightly slower. It's only when test1 runs first that it seems faster. Can anyone reproduce this or have any insights?

    – Sperr
    Aug 4 '17 at 20:25






  • 2





    It might be because the cost of HotSpot compilation is incurred by whatever test is run first.

    – DaBlick
    Nov 14 '18 at 19:32



















0














This is the result of the 6 different combinations of the sample test shared by @Hank D
It's evident that predicate of form u -> exp1 && exp2 is highly performant in all the cases.



one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=3372, min=31, average=33.720000, max=47}
two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9150, min=85, average=91.500000, max=118}
one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9046, min=81, average=90.460000, max=150}

one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8336, min=77, average=83.360000, max=189}
one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9094, min=84, average=90.940000, max=176}
two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10501, min=99, average=105.010000, max=136}

two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=11117, min=98, average=111.170000, max=238}
one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8346, min=77, average=83.460000, max=113}
one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9089, min=81, average=90.890000, max=137}

two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10434, min=98, average=104.340000, max=132}
one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9113, min=81, average=91.130000, max=179}
one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8258, min=77, average=82.580000, max=100}

one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9131, min=81, average=91.310000, max=139}
two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10265, min=97, average=102.650000, max=131}
one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8442, min=77, average=84.420000, max=156}

one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8553, min=81, average=85.530000, max=125}
one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8219, min=77, average=82.190000, max=142}
two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10305, min=97, average=103.050000, max=132}





share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f24054773%2fjava-8-streams-multiple-filters-vs-complex-condition%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    111














    The code that has to be executed for both alternatives is so similar that you can’t predict a result reliably. The underlying object structure might differ but that’s no challenge to the hotspot optimizer. So it depends on other surrounding conditions which will yield to a faster execution, if there is any difference.



    Combining two filter instances creates more objects and hence more delegating code but this can change if you use method references rather than lambda expressions, e.g. replace filter(x -> x.isCool()) by filter(ItemType::isCool). That way you have eliminated the synthetic delegating method created for your lambda expression. So combining two filters using two method references might create the same or lesser delegation code than a single filter invocation using a lambda expression with &&.



    But, as said, this kind of overhead will be eliminated by the HotSpot optimizer and is negligible.



    In theory, two filters could be easier parallelized than a single filter but that’s only relevant for rather computational intense tasks¹.



    So there is no simple answer.



    The bottom line is, don’t think about such performance differences below the odor detection threshold. Use what is more readable.





    ¹…and would require an implementation doing parallel processing of subsequent stages, a road currently not taken by the standard Stream implementation






    share|improve this answer





















    • 2





      doesn't the code have to iterate the resulting stream after each filter?

      – jucardi
      Nov 20 '16 at 23:47






    • 9





      @Juan Carlos Diaz: no, streams don’t work that way. Read about “lazy evaluation”; intermediate operations don’t do anything, they only alter the outcome of the terminal operation.

      – Holger
      Nov 21 '16 at 9:24
















    111














    The code that has to be executed for both alternatives is so similar that you can’t predict a result reliably. The underlying object structure might differ but that’s no challenge to the hotspot optimizer. So it depends on other surrounding conditions which will yield to a faster execution, if there is any difference.



    Combining two filter instances creates more objects and hence more delegating code but this can change if you use method references rather than lambda expressions, e.g. replace filter(x -> x.isCool()) by filter(ItemType::isCool). That way you have eliminated the synthetic delegating method created for your lambda expression. So combining two filters using two method references might create the same or lesser delegation code than a single filter invocation using a lambda expression with &&.



    But, as said, this kind of overhead will be eliminated by the HotSpot optimizer and is negligible.



    In theory, two filters could be easier parallelized than a single filter but that’s only relevant for rather computational intense tasks¹.



    So there is no simple answer.



    The bottom line is, don’t think about such performance differences below the odor detection threshold. Use what is more readable.





    ¹…and would require an implementation doing parallel processing of subsequent stages, a road currently not taken by the standard Stream implementation






    share|improve this answer





















    • 2





      doesn't the code have to iterate the resulting stream after each filter?

      – jucardi
      Nov 20 '16 at 23:47






    • 9





      @Juan Carlos Diaz: no, streams don’t work that way. Read about “lazy evaluation”; intermediate operations don’t do anything, they only alter the outcome of the terminal operation.

      – Holger
      Nov 21 '16 at 9:24














    111












    111








    111







    The code that has to be executed for both alternatives is so similar that you can’t predict a result reliably. The underlying object structure might differ but that’s no challenge to the hotspot optimizer. So it depends on other surrounding conditions which will yield to a faster execution, if there is any difference.



    Combining two filter instances creates more objects and hence more delegating code but this can change if you use method references rather than lambda expressions, e.g. replace filter(x -> x.isCool()) by filter(ItemType::isCool). That way you have eliminated the synthetic delegating method created for your lambda expression. So combining two filters using two method references might create the same or lesser delegation code than a single filter invocation using a lambda expression with &&.



    But, as said, this kind of overhead will be eliminated by the HotSpot optimizer and is negligible.



    In theory, two filters could be easier parallelized than a single filter but that’s only relevant for rather computational intense tasks¹.



    So there is no simple answer.



    The bottom line is, don’t think about such performance differences below the odor detection threshold. Use what is more readable.





    ¹…and would require an implementation doing parallel processing of subsequent stages, a road currently not taken by the standard Stream implementation






    share|improve this answer















    The code that has to be executed for both alternatives is so similar that you can’t predict a result reliably. The underlying object structure might differ but that’s no challenge to the hotspot optimizer. So it depends on other surrounding conditions which will yield to a faster execution, if there is any difference.



    Combining two filter instances creates more objects and hence more delegating code but this can change if you use method references rather than lambda expressions, e.g. replace filter(x -> x.isCool()) by filter(ItemType::isCool). That way you have eliminated the synthetic delegating method created for your lambda expression. So combining two filters using two method references might create the same or lesser delegation code than a single filter invocation using a lambda expression with &&.



    But, as said, this kind of overhead will be eliminated by the HotSpot optimizer and is negligible.



    In theory, two filters could be easier parallelized than a single filter but that’s only relevant for rather computational intense tasks¹.



    So there is no simple answer.



    The bottom line is, don’t think about such performance differences below the odor detection threshold. Use what is more readable.





    ¹…and would require an implementation doing parallel processing of subsequent stages, a road currently not taken by the standard Stream implementation







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Jan 22 '18 at 14:48

























    answered Jun 5 '14 at 8:20









    HolgerHolger

    164k23232441




    164k23232441








    • 2





      doesn't the code have to iterate the resulting stream after each filter?

      – jucardi
      Nov 20 '16 at 23:47






    • 9





      @Juan Carlos Diaz: no, streams don’t work that way. Read about “lazy evaluation”; intermediate operations don’t do anything, they only alter the outcome of the terminal operation.

      – Holger
      Nov 21 '16 at 9:24














    • 2





      doesn't the code have to iterate the resulting stream after each filter?

      – jucardi
      Nov 20 '16 at 23:47






    • 9





      @Juan Carlos Diaz: no, streams don’t work that way. Read about “lazy evaluation”; intermediate operations don’t do anything, they only alter the outcome of the terminal operation.

      – Holger
      Nov 21 '16 at 9:24








    2




    2





    doesn't the code have to iterate the resulting stream after each filter?

    – jucardi
    Nov 20 '16 at 23:47





    doesn't the code have to iterate the resulting stream after each filter?

    – jucardi
    Nov 20 '16 at 23:47




    9




    9





    @Juan Carlos Diaz: no, streams don’t work that way. Read about “lazy evaluation”; intermediate operations don’t do anything, they only alter the outcome of the terminal operation.

    – Holger
    Nov 21 '16 at 9:24





    @Juan Carlos Diaz: no, streams don’t work that way. Read about “lazy evaluation”; intermediate operations don’t do anything, they only alter the outcome of the terminal operation.

    – Holger
    Nov 21 '16 at 9:24













    20














    This test shows that your second option can perform significantly better. Findings first, then the code:



    one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=4142, min=29, average=41.420000, max=82}
    two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=13315, min=117, average=133.150000, max=153}
    one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10320, min=82, average=103.200000, max=127}


    now the code:



    enum Gender {
    FEMALE,
    MALE
    }

    static class User {
    Gender gender;
    int age;

    public User(Gender gender, int age){
    this.gender = gender;
    this.age = age;
    }

    public Gender getGender() {
    return gender;
    }

    public void setGender(Gender gender) {
    this.gender = gender;
    }

    public int getAge() {
    return age;
    }

    public void setAge(int age) {
    this.age = age;
    }
    }

    static long test1(List<User> users){
    long time1 = System.currentTimeMillis();
    users.stream()
    .filter((u) -> u.getGender() == Gender.FEMALE && u.getAge() % 2 == 0)
    .allMatch(u -> true); // least overhead terminal function I can think of
    long time2 = System.currentTimeMillis();
    return time2 - time1;
    }

    static long test2(List<User> users){
    long time1 = System.currentTimeMillis();
    users.stream()
    .filter(u -> u.getGender() == Gender.FEMALE)
    .filter(u -> u.getAge() % 2 == 0)
    .allMatch(u -> true); // least overhead terminal function I can think of
    long time2 = System.currentTimeMillis();
    return time2 - time1;
    }

    static long test3(List<User> users){
    long time1 = System.currentTimeMillis();
    users.stream()
    .filter(((Predicate<User>) u -> u.getGender() == Gender.FEMALE).and(u -> u.getAge() % 2 == 0))
    .allMatch(u -> true); // least overhead terminal function I can think of
    long time2 = System.currentTimeMillis();
    return time2 - time1;
    }

    public static void main(String... args) {
    int size = 10000000;
    List<User> users =
    IntStream.range(0,size)
    .mapToObj(i -> i % 2 == 0 ? new User(Gender.MALE, i % 100) : new User(Gender.FEMALE, i % 100))
    .collect(Collectors.toCollection(()->new ArrayList<>(size)));
    repeat("one filter with predicate of form u -> exp1 && exp2", users, Temp::test1, 100);
    repeat("two filters with predicates of form u -> exp1", users, Temp::test2, 100);
    repeat("one filter with predicate of form predOne.and(pred2)", users, Temp::test3, 100);
    }

    private static void repeat(String name, List<User> users, ToLongFunction<List<User>> test, int iterations) {
    System.out.println(name + ", list size " + users.size() + ", averaged over " + iterations + " runs: " + IntStream.range(0, iterations)
    .mapToLong(i -> test.applyAsLong(users))
    .summaryStatistics());
    }





    share|improve this answer



















    • 2





      Interesting - when I change the order to run test2 BEFORE test1, test1 runs slightly slower. It's only when test1 runs first that it seems faster. Can anyone reproduce this or have any insights?

      – Sperr
      Aug 4 '17 at 20:25






    • 2





      It might be because the cost of HotSpot compilation is incurred by whatever test is run first.

      – DaBlick
      Nov 14 '18 at 19:32
















    20














    This test shows that your second option can perform significantly better. Findings first, then the code:



    one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=4142, min=29, average=41.420000, max=82}
    two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=13315, min=117, average=133.150000, max=153}
    one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10320, min=82, average=103.200000, max=127}


    now the code:



    enum Gender {
    FEMALE,
    MALE
    }

    static class User {
    Gender gender;
    int age;

    public User(Gender gender, int age){
    this.gender = gender;
    this.age = age;
    }

    public Gender getGender() {
    return gender;
    }

    public void setGender(Gender gender) {
    this.gender = gender;
    }

    public int getAge() {
    return age;
    }

    public void setAge(int age) {
    this.age = age;
    }
    }

    static long test1(List<User> users){
    long time1 = System.currentTimeMillis();
    users.stream()
    .filter((u) -> u.getGender() == Gender.FEMALE && u.getAge() % 2 == 0)
    .allMatch(u -> true); // least overhead terminal function I can think of
    long time2 = System.currentTimeMillis();
    return time2 - time1;
    }

    static long test2(List<User> users){
    long time1 = System.currentTimeMillis();
    users.stream()
    .filter(u -> u.getGender() == Gender.FEMALE)
    .filter(u -> u.getAge() % 2 == 0)
    .allMatch(u -> true); // least overhead terminal function I can think of
    long time2 = System.currentTimeMillis();
    return time2 - time1;
    }

    static long test3(List<User> users){
    long time1 = System.currentTimeMillis();
    users.stream()
    .filter(((Predicate<User>) u -> u.getGender() == Gender.FEMALE).and(u -> u.getAge() % 2 == 0))
    .allMatch(u -> true); // least overhead terminal function I can think of
    long time2 = System.currentTimeMillis();
    return time2 - time1;
    }

    public static void main(String... args) {
    int size = 10000000;
    List<User> users =
    IntStream.range(0,size)
    .mapToObj(i -> i % 2 == 0 ? new User(Gender.MALE, i % 100) : new User(Gender.FEMALE, i % 100))
    .collect(Collectors.toCollection(()->new ArrayList<>(size)));
    repeat("one filter with predicate of form u -> exp1 && exp2", users, Temp::test1, 100);
    repeat("two filters with predicates of form u -> exp1", users, Temp::test2, 100);
    repeat("one filter with predicate of form predOne.and(pred2)", users, Temp::test3, 100);
    }

    private static void repeat(String name, List<User> users, ToLongFunction<List<User>> test, int iterations) {
    System.out.println(name + ", list size " + users.size() + ", averaged over " + iterations + " runs: " + IntStream.range(0, iterations)
    .mapToLong(i -> test.applyAsLong(users))
    .summaryStatistics());
    }





    share|improve this answer



















    • 2





      Interesting - when I change the order to run test2 BEFORE test1, test1 runs slightly slower. It's only when test1 runs first that it seems faster. Can anyone reproduce this or have any insights?

      – Sperr
      Aug 4 '17 at 20:25






    • 2





      It might be because the cost of HotSpot compilation is incurred by whatever test is run first.

      – DaBlick
      Nov 14 '18 at 19:32














    20












    20








    20







    This test shows that your second option can perform significantly better. Findings first, then the code:



    one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=4142, min=29, average=41.420000, max=82}
    two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=13315, min=117, average=133.150000, max=153}
    one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10320, min=82, average=103.200000, max=127}


    now the code:



    enum Gender {
    FEMALE,
    MALE
    }

    static class User {
    Gender gender;
    int age;

    public User(Gender gender, int age){
    this.gender = gender;
    this.age = age;
    }

    public Gender getGender() {
    return gender;
    }

    public void setGender(Gender gender) {
    this.gender = gender;
    }

    public int getAge() {
    return age;
    }

    public void setAge(int age) {
    this.age = age;
    }
    }

    static long test1(List<User> users){
    long time1 = System.currentTimeMillis();
    users.stream()
    .filter((u) -> u.getGender() == Gender.FEMALE && u.getAge() % 2 == 0)
    .allMatch(u -> true); // least overhead terminal function I can think of
    long time2 = System.currentTimeMillis();
    return time2 - time1;
    }

    static long test2(List<User> users){
    long time1 = System.currentTimeMillis();
    users.stream()
    .filter(u -> u.getGender() == Gender.FEMALE)
    .filter(u -> u.getAge() % 2 == 0)
    .allMatch(u -> true); // least overhead terminal function I can think of
    long time2 = System.currentTimeMillis();
    return time2 - time1;
    }

    static long test3(List<User> users){
    long time1 = System.currentTimeMillis();
    users.stream()
    .filter(((Predicate<User>) u -> u.getGender() == Gender.FEMALE).and(u -> u.getAge() % 2 == 0))
    .allMatch(u -> true); // least overhead terminal function I can think of
    long time2 = System.currentTimeMillis();
    return time2 - time1;
    }

    public static void main(String... args) {
    int size = 10000000;
    List<User> users =
    IntStream.range(0,size)
    .mapToObj(i -> i % 2 == 0 ? new User(Gender.MALE, i % 100) : new User(Gender.FEMALE, i % 100))
    .collect(Collectors.toCollection(()->new ArrayList<>(size)));
    repeat("one filter with predicate of form u -> exp1 && exp2", users, Temp::test1, 100);
    repeat("two filters with predicates of form u -> exp1", users, Temp::test2, 100);
    repeat("one filter with predicate of form predOne.and(pred2)", users, Temp::test3, 100);
    }

    private static void repeat(String name, List<User> users, ToLongFunction<List<User>> test, int iterations) {
    System.out.println(name + ", list size " + users.size() + ", averaged over " + iterations + " runs: " + IntStream.range(0, iterations)
    .mapToLong(i -> test.applyAsLong(users))
    .summaryStatistics());
    }





    share|improve this answer













    This test shows that your second option can perform significantly better. Findings first, then the code:



    one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=4142, min=29, average=41.420000, max=82}
    two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=13315, min=117, average=133.150000, max=153}
    one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10320, min=82, average=103.200000, max=127}


    now the code:



    enum Gender {
    FEMALE,
    MALE
    }

    static class User {
    Gender gender;
    int age;

    public User(Gender gender, int age){
    this.gender = gender;
    this.age = age;
    }

    public Gender getGender() {
    return gender;
    }

    public void setGender(Gender gender) {
    this.gender = gender;
    }

    public int getAge() {
    return age;
    }

    public void setAge(int age) {
    this.age = age;
    }
    }

    static long test1(List<User> users){
    long time1 = System.currentTimeMillis();
    users.stream()
    .filter((u) -> u.getGender() == Gender.FEMALE && u.getAge() % 2 == 0)
    .allMatch(u -> true); // least overhead terminal function I can think of
    long time2 = System.currentTimeMillis();
    return time2 - time1;
    }

    static long test2(List<User> users){
    long time1 = System.currentTimeMillis();
    users.stream()
    .filter(u -> u.getGender() == Gender.FEMALE)
    .filter(u -> u.getAge() % 2 == 0)
    .allMatch(u -> true); // least overhead terminal function I can think of
    long time2 = System.currentTimeMillis();
    return time2 - time1;
    }

    static long test3(List<User> users){
    long time1 = System.currentTimeMillis();
    users.stream()
    .filter(((Predicate<User>) u -> u.getGender() == Gender.FEMALE).and(u -> u.getAge() % 2 == 0))
    .allMatch(u -> true); // least overhead terminal function I can think of
    long time2 = System.currentTimeMillis();
    return time2 - time1;
    }

    public static void main(String... args) {
    int size = 10000000;
    List<User> users =
    IntStream.range(0,size)
    .mapToObj(i -> i % 2 == 0 ? new User(Gender.MALE, i % 100) : new User(Gender.FEMALE, i % 100))
    .collect(Collectors.toCollection(()->new ArrayList<>(size)));
    repeat("one filter with predicate of form u -> exp1 && exp2", users, Temp::test1, 100);
    repeat("two filters with predicates of form u -> exp1", users, Temp::test2, 100);
    repeat("one filter with predicate of form predOne.and(pred2)", users, Temp::test3, 100);
    }

    private static void repeat(String name, List<User> users, ToLongFunction<List<User>> test, int iterations) {
    System.out.println(name + ", list size " + users.size() + ", averaged over " + iterations + " runs: " + IntStream.range(0, iterations)
    .mapToLong(i -> test.applyAsLong(users))
    .summaryStatistics());
    }






    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Apr 9 '16 at 20:05









    Hank DHank D

    4,39221430




    4,39221430








    • 2





      Interesting - when I change the order to run test2 BEFORE test1, test1 runs slightly slower. It's only when test1 runs first that it seems faster. Can anyone reproduce this or have any insights?

      – Sperr
      Aug 4 '17 at 20:25






    • 2





      It might be because the cost of HotSpot compilation is incurred by whatever test is run first.

      – DaBlick
      Nov 14 '18 at 19:32














    • 2





      Interesting - when I change the order to run test2 BEFORE test1, test1 runs slightly slower. It's only when test1 runs first that it seems faster. Can anyone reproduce this or have any insights?

      – Sperr
      Aug 4 '17 at 20:25






    • 2





      It might be because the cost of HotSpot compilation is incurred by whatever test is run first.

      – DaBlick
      Nov 14 '18 at 19:32








    2




    2





    Interesting - when I change the order to run test2 BEFORE test1, test1 runs slightly slower. It's only when test1 runs first that it seems faster. Can anyone reproduce this or have any insights?

    – Sperr
    Aug 4 '17 at 20:25





    Interesting - when I change the order to run test2 BEFORE test1, test1 runs slightly slower. It's only when test1 runs first that it seems faster. Can anyone reproduce this or have any insights?

    – Sperr
    Aug 4 '17 at 20:25




    2




    2





    It might be because the cost of HotSpot compilation is incurred by whatever test is run first.

    – DaBlick
    Nov 14 '18 at 19:32





    It might be because the cost of HotSpot compilation is incurred by whatever test is run first.

    – DaBlick
    Nov 14 '18 at 19:32











    0














    This is the result of the 6 different combinations of the sample test shared by @Hank D
    It's evident that predicate of form u -> exp1 && exp2 is highly performant in all the cases.



    one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=3372, min=31, average=33.720000, max=47}
    two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9150, min=85, average=91.500000, max=118}
    one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9046, min=81, average=90.460000, max=150}

    one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8336, min=77, average=83.360000, max=189}
    one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9094, min=84, average=90.940000, max=176}
    two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10501, min=99, average=105.010000, max=136}

    two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=11117, min=98, average=111.170000, max=238}
    one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8346, min=77, average=83.460000, max=113}
    one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9089, min=81, average=90.890000, max=137}

    two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10434, min=98, average=104.340000, max=132}
    one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9113, min=81, average=91.130000, max=179}
    one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8258, min=77, average=82.580000, max=100}

    one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9131, min=81, average=91.310000, max=139}
    two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10265, min=97, average=102.650000, max=131}
    one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8442, min=77, average=84.420000, max=156}

    one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8553, min=81, average=85.530000, max=125}
    one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8219, min=77, average=82.190000, max=142}
    two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10305, min=97, average=103.050000, max=132}





    share|improve this answer




























      0














      This is the result of the 6 different combinations of the sample test shared by @Hank D
      It's evident that predicate of form u -> exp1 && exp2 is highly performant in all the cases.



      one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=3372, min=31, average=33.720000, max=47}
      two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9150, min=85, average=91.500000, max=118}
      one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9046, min=81, average=90.460000, max=150}

      one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8336, min=77, average=83.360000, max=189}
      one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9094, min=84, average=90.940000, max=176}
      two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10501, min=99, average=105.010000, max=136}

      two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=11117, min=98, average=111.170000, max=238}
      one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8346, min=77, average=83.460000, max=113}
      one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9089, min=81, average=90.890000, max=137}

      two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10434, min=98, average=104.340000, max=132}
      one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9113, min=81, average=91.130000, max=179}
      one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8258, min=77, average=82.580000, max=100}

      one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9131, min=81, average=91.310000, max=139}
      two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10265, min=97, average=102.650000, max=131}
      one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8442, min=77, average=84.420000, max=156}

      one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8553, min=81, average=85.530000, max=125}
      one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8219, min=77, average=82.190000, max=142}
      two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10305, min=97, average=103.050000, max=132}





      share|improve this answer


























        0












        0








        0







        This is the result of the 6 different combinations of the sample test shared by @Hank D
        It's evident that predicate of form u -> exp1 && exp2 is highly performant in all the cases.



        one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=3372, min=31, average=33.720000, max=47}
        two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9150, min=85, average=91.500000, max=118}
        one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9046, min=81, average=90.460000, max=150}

        one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8336, min=77, average=83.360000, max=189}
        one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9094, min=84, average=90.940000, max=176}
        two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10501, min=99, average=105.010000, max=136}

        two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=11117, min=98, average=111.170000, max=238}
        one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8346, min=77, average=83.460000, max=113}
        one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9089, min=81, average=90.890000, max=137}

        two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10434, min=98, average=104.340000, max=132}
        one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9113, min=81, average=91.130000, max=179}
        one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8258, min=77, average=82.580000, max=100}

        one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9131, min=81, average=91.310000, max=139}
        two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10265, min=97, average=102.650000, max=131}
        one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8442, min=77, average=84.420000, max=156}

        one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8553, min=81, average=85.530000, max=125}
        one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8219, min=77, average=82.190000, max=142}
        two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10305, min=97, average=103.050000, max=132}





        share|improve this answer













        This is the result of the 6 different combinations of the sample test shared by @Hank D
        It's evident that predicate of form u -> exp1 && exp2 is highly performant in all the cases.



        one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=3372, min=31, average=33.720000, max=47}
        two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9150, min=85, average=91.500000, max=118}
        one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9046, min=81, average=90.460000, max=150}

        one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8336, min=77, average=83.360000, max=189}
        one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9094, min=84, average=90.940000, max=176}
        two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10501, min=99, average=105.010000, max=136}

        two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=11117, min=98, average=111.170000, max=238}
        one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8346, min=77, average=83.460000, max=113}
        one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9089, min=81, average=90.890000, max=137}

        two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10434, min=98, average=104.340000, max=132}
        one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9113, min=81, average=91.130000, max=179}
        one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8258, min=77, average=82.580000, max=100}

        one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=9131, min=81, average=91.310000, max=139}
        two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10265, min=97, average=102.650000, max=131}
        one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8442, min=77, average=84.420000, max=156}

        one filter with predicate of form predOne.and(pred2), list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8553, min=81, average=85.530000, max=125}
        one filter with predicate of form u -> exp1 && exp2, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=8219, min=77, average=82.190000, max=142}
        two filters with predicates of form u -> exp1, list size 10000000, averaged over 100 runs: LongSummaryStatistics{count=100, sum=10305, min=97, average=103.050000, max=132}






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 13 '18 at 20:54









        Venkat MadhavVenkat Madhav

        54211017




        54211017






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f24054773%2fjava-8-streams-multiple-filters-vs-complex-condition%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            List item for chat from Array inside array React Native

            Thiostrepton

            Caerphilly