Java 8 Streams: multiple filters vs. complex condition
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
add a comment |
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
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
add a comment |
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
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
java lambda filter java-8 java-stream
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
add a comment |
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
add a comment |
3 Answers
3
active
oldest
votes
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
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
add a comment |
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());
}
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
add a comment |
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}
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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
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
add a comment |
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
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
add a comment |
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
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
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
add a comment |
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
add a comment |
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());
}
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
add a comment |
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());
}
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
add a comment |
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());
}
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());
}
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
add a comment |
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
add a comment |
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}
add a comment |
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}
add a comment |
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}
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}
answered Nov 13 '18 at 20:54
Venkat MadhavVenkat Madhav
54211017
54211017
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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