Why AtomicInteger based Stream solutions are not recommended?
Say I have this list of fruits:-
List<String> f = Arrays.asList("Banana", "Apple", "Grape", "Orange", "Kiwi");
I need to prepend a serial number to each fruit and print it. The order of fruit or serial number does not matter. So this is a valid output:-
4. Kiwi
3. Orange
1. Grape
2. Apple
5. Banana
Solution #1
AtomicInteger number = new AtomicInteger(0);
String result = f.parallelStream()
.map(i -> String.format("%d. %s", number.incrementAndGet(), i))
.collect(Collectors.joining("n"));
Solution #2
String result = IntStream.rangeClosed(1, f.size())
.parallel()
.mapToObj(i -> String.format("%d. %s", i, f.get(i - 1)))
.collect(Collectors.joining("n"));
Question
Why is solution #1 a bad practice? I have seen at a lot of places that AtomicInteger
based solutions are bad (like in this answer), specially in parallel stream processing (that's the reason I used parallel streams above, to try run into issues).
I looked at these questions/answers:-
In which cases Stream operations should be stateful?
Is use of AtomicInteger for indexing in Stream a legit way?
Java 8: Preferred way to count iterations of a lambda?
They just mention (unless I missed something) "unexpected results can occur". Like what? Can it happen in this example? If not, can you provide me an example where it can happen?
As for "no guarantees are made as to the order in which the mapper function is applied", well, that's the nature of parallel processing, so I accept it, and also, the order doesn't matter in this particular example.
AtomicInteger
is thread safe, so it shouldn't be a problem in parallel processing.
Can someone provide examples in which cases there will be issues while using such a state-based solution?
java java-8 thread-safety java-stream atomicinteger
add a comment |
Say I have this list of fruits:-
List<String> f = Arrays.asList("Banana", "Apple", "Grape", "Orange", "Kiwi");
I need to prepend a serial number to each fruit and print it. The order of fruit or serial number does not matter. So this is a valid output:-
4. Kiwi
3. Orange
1. Grape
2. Apple
5. Banana
Solution #1
AtomicInteger number = new AtomicInteger(0);
String result = f.parallelStream()
.map(i -> String.format("%d. %s", number.incrementAndGet(), i))
.collect(Collectors.joining("n"));
Solution #2
String result = IntStream.rangeClosed(1, f.size())
.parallel()
.mapToObj(i -> String.format("%d. %s", i, f.get(i - 1)))
.collect(Collectors.joining("n"));
Question
Why is solution #1 a bad practice? I have seen at a lot of places that AtomicInteger
based solutions are bad (like in this answer), specially in parallel stream processing (that's the reason I used parallel streams above, to try run into issues).
I looked at these questions/answers:-
In which cases Stream operations should be stateful?
Is use of AtomicInteger for indexing in Stream a legit way?
Java 8: Preferred way to count iterations of a lambda?
They just mention (unless I missed something) "unexpected results can occur". Like what? Can it happen in this example? If not, can you provide me an example where it can happen?
As for "no guarantees are made as to the order in which the mapper function is applied", well, that's the nature of parallel processing, so I accept it, and also, the order doesn't matter in this particular example.
AtomicInteger
is thread safe, so it shouldn't be a problem in parallel processing.
Can someone provide examples in which cases there will be issues while using such a state-based solution?
java java-8 thread-safety java-stream atomicinteger
6
tldr: Side-effects are "icky", even if "thread-safe". The ordering argument is very relevant in more generalized cases: eg. instead of adding integers (x + y == y + x), what if it was concatenating strings (concat(x,y) != concat(y,x))? There is much less chance to accidentally introduce such if side-effects are avoided.
– user2864740
Nov 16 '18 at 0:42
2
It's unclear from the docs whether statelessness is required or only recommended. I personally see no problem with #1, though I can imagine #2 performing better.
– shmosel
Nov 16 '18 at 0:45
2
Streams come from functional programming, where you should ideally have no side effects. Sometimes this isn't possible, but if there is a simple way to achieve the same thing without side effects you should use that.
– Peter Lawrey
Nov 16 '18 at 8:53
4
Well, when you are fine with a result where the numbers are neither, in order nor reflecting the source element order, the only remaining issue is that it is inefficient, compared to the recommended approach. But most other Q&As are about tasks not being fine with such a wrong order and well, when you use this pattern in these rare case where you are fine with a meaningless number, it might soon become a habit…
– Holger
Nov 16 '18 at 9:05
add a comment |
Say I have this list of fruits:-
List<String> f = Arrays.asList("Banana", "Apple", "Grape", "Orange", "Kiwi");
I need to prepend a serial number to each fruit and print it. The order of fruit or serial number does not matter. So this is a valid output:-
4. Kiwi
3. Orange
1. Grape
2. Apple
5. Banana
Solution #1
AtomicInteger number = new AtomicInteger(0);
String result = f.parallelStream()
.map(i -> String.format("%d. %s", number.incrementAndGet(), i))
.collect(Collectors.joining("n"));
Solution #2
String result = IntStream.rangeClosed(1, f.size())
.parallel()
.mapToObj(i -> String.format("%d. %s", i, f.get(i - 1)))
.collect(Collectors.joining("n"));
Question
Why is solution #1 a bad practice? I have seen at a lot of places that AtomicInteger
based solutions are bad (like in this answer), specially in parallel stream processing (that's the reason I used parallel streams above, to try run into issues).
I looked at these questions/answers:-
In which cases Stream operations should be stateful?
Is use of AtomicInteger for indexing in Stream a legit way?
Java 8: Preferred way to count iterations of a lambda?
They just mention (unless I missed something) "unexpected results can occur". Like what? Can it happen in this example? If not, can you provide me an example where it can happen?
As for "no guarantees are made as to the order in which the mapper function is applied", well, that's the nature of parallel processing, so I accept it, and also, the order doesn't matter in this particular example.
AtomicInteger
is thread safe, so it shouldn't be a problem in parallel processing.
Can someone provide examples in which cases there will be issues while using such a state-based solution?
java java-8 thread-safety java-stream atomicinteger
Say I have this list of fruits:-
List<String> f = Arrays.asList("Banana", "Apple", "Grape", "Orange", "Kiwi");
I need to prepend a serial number to each fruit and print it. The order of fruit or serial number does not matter. So this is a valid output:-
4. Kiwi
3. Orange
1. Grape
2. Apple
5. Banana
Solution #1
AtomicInteger number = new AtomicInteger(0);
String result = f.parallelStream()
.map(i -> String.format("%d. %s", number.incrementAndGet(), i))
.collect(Collectors.joining("n"));
Solution #2
String result = IntStream.rangeClosed(1, f.size())
.parallel()
.mapToObj(i -> String.format("%d. %s", i, f.get(i - 1)))
.collect(Collectors.joining("n"));
Question
Why is solution #1 a bad practice? I have seen at a lot of places that AtomicInteger
based solutions are bad (like in this answer), specially in parallel stream processing (that's the reason I used parallel streams above, to try run into issues).
I looked at these questions/answers:-
In which cases Stream operations should be stateful?
Is use of AtomicInteger for indexing in Stream a legit way?
Java 8: Preferred way to count iterations of a lambda?
They just mention (unless I missed something) "unexpected results can occur". Like what? Can it happen in this example? If not, can you provide me an example where it can happen?
As for "no guarantees are made as to the order in which the mapper function is applied", well, that's the nature of parallel processing, so I accept it, and also, the order doesn't matter in this particular example.
AtomicInteger
is thread safe, so it shouldn't be a problem in parallel processing.
Can someone provide examples in which cases there will be issues while using such a state-based solution?
java java-8 thread-safety java-stream atomicinteger
java java-8 thread-safety java-stream atomicinteger
edited Nov 16 '18 at 5:13
Andrew Tobilko
28.3k104588
28.3k104588
asked Nov 16 '18 at 0:37
KartikKartik
4,19731537
4,19731537
6
tldr: Side-effects are "icky", even if "thread-safe". The ordering argument is very relevant in more generalized cases: eg. instead of adding integers (x + y == y + x), what if it was concatenating strings (concat(x,y) != concat(y,x))? There is much less chance to accidentally introduce such if side-effects are avoided.
– user2864740
Nov 16 '18 at 0:42
2
It's unclear from the docs whether statelessness is required or only recommended. I personally see no problem with #1, though I can imagine #2 performing better.
– shmosel
Nov 16 '18 at 0:45
2
Streams come from functional programming, where you should ideally have no side effects. Sometimes this isn't possible, but if there is a simple way to achieve the same thing without side effects you should use that.
– Peter Lawrey
Nov 16 '18 at 8:53
4
Well, when you are fine with a result where the numbers are neither, in order nor reflecting the source element order, the only remaining issue is that it is inefficient, compared to the recommended approach. But most other Q&As are about tasks not being fine with such a wrong order and well, when you use this pattern in these rare case where you are fine with a meaningless number, it might soon become a habit…
– Holger
Nov 16 '18 at 9:05
add a comment |
6
tldr: Side-effects are "icky", even if "thread-safe". The ordering argument is very relevant in more generalized cases: eg. instead of adding integers (x + y == y + x), what if it was concatenating strings (concat(x,y) != concat(y,x))? There is much less chance to accidentally introduce such if side-effects are avoided.
– user2864740
Nov 16 '18 at 0:42
2
It's unclear from the docs whether statelessness is required or only recommended. I personally see no problem with #1, though I can imagine #2 performing better.
– shmosel
Nov 16 '18 at 0:45
2
Streams come from functional programming, where you should ideally have no side effects. Sometimes this isn't possible, but if there is a simple way to achieve the same thing without side effects you should use that.
– Peter Lawrey
Nov 16 '18 at 8:53
4
Well, when you are fine with a result where the numbers are neither, in order nor reflecting the source element order, the only remaining issue is that it is inefficient, compared to the recommended approach. But most other Q&As are about tasks not being fine with such a wrong order and well, when you use this pattern in these rare case where you are fine with a meaningless number, it might soon become a habit…
– Holger
Nov 16 '18 at 9:05
6
6
tldr: Side-effects are "icky", even if "thread-safe". The ordering argument is very relevant in more generalized cases: eg. instead of adding integers (x + y == y + x), what if it was concatenating strings (concat(x,y) != concat(y,x))? There is much less chance to accidentally introduce such if side-effects are avoided.
– user2864740
Nov 16 '18 at 0:42
tldr: Side-effects are "icky", even if "thread-safe". The ordering argument is very relevant in more generalized cases: eg. instead of adding integers (x + y == y + x), what if it was concatenating strings (concat(x,y) != concat(y,x))? There is much less chance to accidentally introduce such if side-effects are avoided.
– user2864740
Nov 16 '18 at 0:42
2
2
It's unclear from the docs whether statelessness is required or only recommended. I personally see no problem with #1, though I can imagine #2 performing better.
– shmosel
Nov 16 '18 at 0:45
It's unclear from the docs whether statelessness is required or only recommended. I personally see no problem with #1, though I can imagine #2 performing better.
– shmosel
Nov 16 '18 at 0:45
2
2
Streams come from functional programming, where you should ideally have no side effects. Sometimes this isn't possible, but if there is a simple way to achieve the same thing without side effects you should use that.
– Peter Lawrey
Nov 16 '18 at 8:53
Streams come from functional programming, where you should ideally have no side effects. Sometimes this isn't possible, but if there is a simple way to achieve the same thing without side effects you should use that.
– Peter Lawrey
Nov 16 '18 at 8:53
4
4
Well, when you are fine with a result where the numbers are neither, in order nor reflecting the source element order, the only remaining issue is that it is inefficient, compared to the recommended approach. But most other Q&As are about tasks not being fine with such a wrong order and well, when you use this pattern in these rare case where you are fine with a meaningless number, it might soon become a habit…
– Holger
Nov 16 '18 at 9:05
Well, when you are fine with a result where the numbers are neither, in order nor reflecting the source element order, the only remaining issue is that it is inefficient, compared to the recommended approach. But most other Q&As are about tasks not being fine with such a wrong order and well, when you use this pattern in these rare case where you are fine with a meaningless number, it might soon become a habit…
– Holger
Nov 16 '18 at 9:05
add a comment |
3 Answers
3
active
oldest
votes
Note also that attempting to access mutable state from behavioral parameters presents you with a bad choice with respect to safety and performance; if you do not synchronize access to that state, you have a data race and therefore your code is broken, but if you do synchronize access to that state, you risk having contention undermine the parallelism you are seeking to benefit from. The best approach is to avoid stateful behavioral parameters to stream operations entirely; there is usually a way to restructure the stream pipeline to avoid statefulness.
Package
java.util.stream
, Stateless behaviors
From the perspective of thread-safety and correctness, there is nothing wrong with solution 1. Performance (as an advantage of parallel processing) might suffer, though.
Why is solution #1 a bad practice?
I wouldn't say it's a bad practice or something unacceptable. It's simply not recommended for the sake of performance.
They just mention (unless I missed something) "unexpected results can occur". Like what?
"Unexpected results" is a very broad term, and usually refers to improper synchronisation, "What's the hell just happened?"-like behaviour.
Can it happen in this example?
It's not the case. You are likely not going to run into issues.
If not, can you provide me an example where it can happen?
Change the AtomicInteger
to an int
*, replace number.incrementAndGet()
with ++number
, and you will have one.
*a boxed int
(e.g. wrapper-based, array-based) so you can work with it within a lambda
add a comment |
Well look at what the answer from Stuart Marks here - he is using a stateful predicate.
The are a couple of potential problems, but if you don't care about them or really understand them - you should be fine.
First is order, exhibited under the current implementation for parallel processing, but if you don't care about order, like in your example, you are ok.
Second one is potential speed AtomicInteger
will be times slower to increment that a simple int, as said, if you care about this.
Third one is more subtle. Sometimes there is no guarantee that map
will be executed, at all, for example since java-9:
someStream.map(i -> /* do something with i and numbers */)
.count();
The point here is that since you are counting, there is no need to do the mapping, so its skipped. In general, the elements that hit some intermediate operation are not guaranteed to get to the terminal one. Imagine a map.filter.map
situation, the first map might "see" more elements compared to the second one, because some elements might be filtered. So it's not recommended to rely on this, unless you can reason exactly what is going on.
In your example, IMO, you are more than safe to do what you do; but if you slightly change your code, this requires additional reasoning to prove it's correctness. I would go with solution 2, just because it's a lot easier to understand for me and it does not have the potential problems listed above.
add a comment |
Case 2 - In API notes of IntStream class returns a sequential ordered IntStream from startInclusive (inclusive) to endInclusive (inclusive) by an incremental step of 1 kind of for loop thus parallel stream are processing it one by one and providing the correct order.
* @param startInclusive the (inclusive) initial value
* @param endInclusive the inclusive upper bound
* @return a sequential {@code IntStream} for the range of {@code int}
* elements
*/
public static IntStream rangeClosed(int startInclusive, int endInclusive) {
Case 1 - It is obvious that the list will be processed in parallel thus the order will not be correct. Since mapping operation is performed in parallel, the results for the same input could vary from run to run, due to thread scheduling differences thus no guarantees that different operations on the "same" element within the same stream pipeline are executed in the same thread also there is no guarantee how a mapper function is also applied to the particular elements within the stream.
Source Java Doc
@Kartik: There is no guarantees that different operations on the "same" element within the same stream pipeline are executed in the same thread. So there is no guarantee how a mapper function is also applied to the particular elements within the stream or in what thread any behavioral parameter is executed.Thus result might be unexpected as well.
– zack
Nov 16 '18 at 4:20
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%2f53329809%2fwhy-atomicinteger-based-stream-solutions-are-not-recommended%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
Note also that attempting to access mutable state from behavioral parameters presents you with a bad choice with respect to safety and performance; if you do not synchronize access to that state, you have a data race and therefore your code is broken, but if you do synchronize access to that state, you risk having contention undermine the parallelism you are seeking to benefit from. The best approach is to avoid stateful behavioral parameters to stream operations entirely; there is usually a way to restructure the stream pipeline to avoid statefulness.
Package
java.util.stream
, Stateless behaviors
From the perspective of thread-safety and correctness, there is nothing wrong with solution 1. Performance (as an advantage of parallel processing) might suffer, though.
Why is solution #1 a bad practice?
I wouldn't say it's a bad practice or something unacceptable. It's simply not recommended for the sake of performance.
They just mention (unless I missed something) "unexpected results can occur". Like what?
"Unexpected results" is a very broad term, and usually refers to improper synchronisation, "What's the hell just happened?"-like behaviour.
Can it happen in this example?
It's not the case. You are likely not going to run into issues.
If not, can you provide me an example where it can happen?
Change the AtomicInteger
to an int
*, replace number.incrementAndGet()
with ++number
, and you will have one.
*a boxed int
(e.g. wrapper-based, array-based) so you can work with it within a lambda
add a comment |
Note also that attempting to access mutable state from behavioral parameters presents you with a bad choice with respect to safety and performance; if you do not synchronize access to that state, you have a data race and therefore your code is broken, but if you do synchronize access to that state, you risk having contention undermine the parallelism you are seeking to benefit from. The best approach is to avoid stateful behavioral parameters to stream operations entirely; there is usually a way to restructure the stream pipeline to avoid statefulness.
Package
java.util.stream
, Stateless behaviors
From the perspective of thread-safety and correctness, there is nothing wrong with solution 1. Performance (as an advantage of parallel processing) might suffer, though.
Why is solution #1 a bad practice?
I wouldn't say it's a bad practice or something unacceptable. It's simply not recommended for the sake of performance.
They just mention (unless I missed something) "unexpected results can occur". Like what?
"Unexpected results" is a very broad term, and usually refers to improper synchronisation, "What's the hell just happened?"-like behaviour.
Can it happen in this example?
It's not the case. You are likely not going to run into issues.
If not, can you provide me an example where it can happen?
Change the AtomicInteger
to an int
*, replace number.incrementAndGet()
with ++number
, and you will have one.
*a boxed int
(e.g. wrapper-based, array-based) so you can work with it within a lambda
add a comment |
Note also that attempting to access mutable state from behavioral parameters presents you with a bad choice with respect to safety and performance; if you do not synchronize access to that state, you have a data race and therefore your code is broken, but if you do synchronize access to that state, you risk having contention undermine the parallelism you are seeking to benefit from. The best approach is to avoid stateful behavioral parameters to stream operations entirely; there is usually a way to restructure the stream pipeline to avoid statefulness.
Package
java.util.stream
, Stateless behaviors
From the perspective of thread-safety and correctness, there is nothing wrong with solution 1. Performance (as an advantage of parallel processing) might suffer, though.
Why is solution #1 a bad practice?
I wouldn't say it's a bad practice or something unacceptable. It's simply not recommended for the sake of performance.
They just mention (unless I missed something) "unexpected results can occur". Like what?
"Unexpected results" is a very broad term, and usually refers to improper synchronisation, "What's the hell just happened?"-like behaviour.
Can it happen in this example?
It's not the case. You are likely not going to run into issues.
If not, can you provide me an example where it can happen?
Change the AtomicInteger
to an int
*, replace number.incrementAndGet()
with ++number
, and you will have one.
*a boxed int
(e.g. wrapper-based, array-based) so you can work with it within a lambda
Note also that attempting to access mutable state from behavioral parameters presents you with a bad choice with respect to safety and performance; if you do not synchronize access to that state, you have a data race and therefore your code is broken, but if you do synchronize access to that state, you risk having contention undermine the parallelism you are seeking to benefit from. The best approach is to avoid stateful behavioral parameters to stream operations entirely; there is usually a way to restructure the stream pipeline to avoid statefulness.
Package
java.util.stream
, Stateless behaviors
From the perspective of thread-safety and correctness, there is nothing wrong with solution 1. Performance (as an advantage of parallel processing) might suffer, though.
Why is solution #1 a bad practice?
I wouldn't say it's a bad practice or something unacceptable. It's simply not recommended for the sake of performance.
They just mention (unless I missed something) "unexpected results can occur". Like what?
"Unexpected results" is a very broad term, and usually refers to improper synchronisation, "What's the hell just happened?"-like behaviour.
Can it happen in this example?
It's not the case. You are likely not going to run into issues.
If not, can you provide me an example where it can happen?
Change the AtomicInteger
to an int
*, replace number.incrementAndGet()
with ++number
, and you will have one.
*a boxed int
(e.g. wrapper-based, array-based) so you can work with it within a lambda
edited Nov 16 '18 at 19:03
answered Nov 16 '18 at 5:33
Andrew TobilkoAndrew Tobilko
28.3k104588
28.3k104588
add a comment |
add a comment |
Well look at what the answer from Stuart Marks here - he is using a stateful predicate.
The are a couple of potential problems, but if you don't care about them or really understand them - you should be fine.
First is order, exhibited under the current implementation for parallel processing, but if you don't care about order, like in your example, you are ok.
Second one is potential speed AtomicInteger
will be times slower to increment that a simple int, as said, if you care about this.
Third one is more subtle. Sometimes there is no guarantee that map
will be executed, at all, for example since java-9:
someStream.map(i -> /* do something with i and numbers */)
.count();
The point here is that since you are counting, there is no need to do the mapping, so its skipped. In general, the elements that hit some intermediate operation are not guaranteed to get to the terminal one. Imagine a map.filter.map
situation, the first map might "see" more elements compared to the second one, because some elements might be filtered. So it's not recommended to rely on this, unless you can reason exactly what is going on.
In your example, IMO, you are more than safe to do what you do; but if you slightly change your code, this requires additional reasoning to prove it's correctness. I would go with solution 2, just because it's a lot easier to understand for me and it does not have the potential problems listed above.
add a comment |
Well look at what the answer from Stuart Marks here - he is using a stateful predicate.
The are a couple of potential problems, but if you don't care about them or really understand them - you should be fine.
First is order, exhibited under the current implementation for parallel processing, but if you don't care about order, like in your example, you are ok.
Second one is potential speed AtomicInteger
will be times slower to increment that a simple int, as said, if you care about this.
Third one is more subtle. Sometimes there is no guarantee that map
will be executed, at all, for example since java-9:
someStream.map(i -> /* do something with i and numbers */)
.count();
The point here is that since you are counting, there is no need to do the mapping, so its skipped. In general, the elements that hit some intermediate operation are not guaranteed to get to the terminal one. Imagine a map.filter.map
situation, the first map might "see" more elements compared to the second one, because some elements might be filtered. So it's not recommended to rely on this, unless you can reason exactly what is going on.
In your example, IMO, you are more than safe to do what you do; but if you slightly change your code, this requires additional reasoning to prove it's correctness. I would go with solution 2, just because it's a lot easier to understand for me and it does not have the potential problems listed above.
add a comment |
Well look at what the answer from Stuart Marks here - he is using a stateful predicate.
The are a couple of potential problems, but if you don't care about them or really understand them - you should be fine.
First is order, exhibited under the current implementation for parallel processing, but if you don't care about order, like in your example, you are ok.
Second one is potential speed AtomicInteger
will be times slower to increment that a simple int, as said, if you care about this.
Third one is more subtle. Sometimes there is no guarantee that map
will be executed, at all, for example since java-9:
someStream.map(i -> /* do something with i and numbers */)
.count();
The point here is that since you are counting, there is no need to do the mapping, so its skipped. In general, the elements that hit some intermediate operation are not guaranteed to get to the terminal one. Imagine a map.filter.map
situation, the first map might "see" more elements compared to the second one, because some elements might be filtered. So it's not recommended to rely on this, unless you can reason exactly what is going on.
In your example, IMO, you are more than safe to do what you do; but if you slightly change your code, this requires additional reasoning to prove it's correctness. I would go with solution 2, just because it's a lot easier to understand for me and it does not have the potential problems listed above.
Well look at what the answer from Stuart Marks here - he is using a stateful predicate.
The are a couple of potential problems, but if you don't care about them or really understand them - you should be fine.
First is order, exhibited under the current implementation for parallel processing, but if you don't care about order, like in your example, you are ok.
Second one is potential speed AtomicInteger
will be times slower to increment that a simple int, as said, if you care about this.
Third one is more subtle. Sometimes there is no guarantee that map
will be executed, at all, for example since java-9:
someStream.map(i -> /* do something with i and numbers */)
.count();
The point here is that since you are counting, there is no need to do the mapping, so its skipped. In general, the elements that hit some intermediate operation are not guaranteed to get to the terminal one. Imagine a map.filter.map
situation, the first map might "see" more elements compared to the second one, because some elements might be filtered. So it's not recommended to rely on this, unless you can reason exactly what is going on.
In your example, IMO, you are more than safe to do what you do; but if you slightly change your code, this requires additional reasoning to prove it's correctness. I would go with solution 2, just because it's a lot easier to understand for me and it does not have the potential problems listed above.
edited Nov 16 '18 at 5:16
Andrew Tobilko
28.3k104588
28.3k104588
answered Nov 16 '18 at 4:50
EugeneEugene
71.4k9103171
71.4k9103171
add a comment |
add a comment |
Case 2 - In API notes of IntStream class returns a sequential ordered IntStream from startInclusive (inclusive) to endInclusive (inclusive) by an incremental step of 1 kind of for loop thus parallel stream are processing it one by one and providing the correct order.
* @param startInclusive the (inclusive) initial value
* @param endInclusive the inclusive upper bound
* @return a sequential {@code IntStream} for the range of {@code int}
* elements
*/
public static IntStream rangeClosed(int startInclusive, int endInclusive) {
Case 1 - It is obvious that the list will be processed in parallel thus the order will not be correct. Since mapping operation is performed in parallel, the results for the same input could vary from run to run, due to thread scheduling differences thus no guarantees that different operations on the "same" element within the same stream pipeline are executed in the same thread also there is no guarantee how a mapper function is also applied to the particular elements within the stream.
Source Java Doc
@Kartik: There is no guarantees that different operations on the "same" element within the same stream pipeline are executed in the same thread. So there is no guarantee how a mapper function is also applied to the particular elements within the stream or in what thread any behavioral parameter is executed.Thus result might be unexpected as well.
– zack
Nov 16 '18 at 4:20
add a comment |
Case 2 - In API notes of IntStream class returns a sequential ordered IntStream from startInclusive (inclusive) to endInclusive (inclusive) by an incremental step of 1 kind of for loop thus parallel stream are processing it one by one and providing the correct order.
* @param startInclusive the (inclusive) initial value
* @param endInclusive the inclusive upper bound
* @return a sequential {@code IntStream} for the range of {@code int}
* elements
*/
public static IntStream rangeClosed(int startInclusive, int endInclusive) {
Case 1 - It is obvious that the list will be processed in parallel thus the order will not be correct. Since mapping operation is performed in parallel, the results for the same input could vary from run to run, due to thread scheduling differences thus no guarantees that different operations on the "same" element within the same stream pipeline are executed in the same thread also there is no guarantee how a mapper function is also applied to the particular elements within the stream.
Source Java Doc
@Kartik: There is no guarantees that different operations on the "same" element within the same stream pipeline are executed in the same thread. So there is no guarantee how a mapper function is also applied to the particular elements within the stream or in what thread any behavioral parameter is executed.Thus result might be unexpected as well.
– zack
Nov 16 '18 at 4:20
add a comment |
Case 2 - In API notes of IntStream class returns a sequential ordered IntStream from startInclusive (inclusive) to endInclusive (inclusive) by an incremental step of 1 kind of for loop thus parallel stream are processing it one by one and providing the correct order.
* @param startInclusive the (inclusive) initial value
* @param endInclusive the inclusive upper bound
* @return a sequential {@code IntStream} for the range of {@code int}
* elements
*/
public static IntStream rangeClosed(int startInclusive, int endInclusive) {
Case 1 - It is obvious that the list will be processed in parallel thus the order will not be correct. Since mapping operation is performed in parallel, the results for the same input could vary from run to run, due to thread scheduling differences thus no guarantees that different operations on the "same" element within the same stream pipeline are executed in the same thread also there is no guarantee how a mapper function is also applied to the particular elements within the stream.
Source Java Doc
Case 2 - In API notes of IntStream class returns a sequential ordered IntStream from startInclusive (inclusive) to endInclusive (inclusive) by an incremental step of 1 kind of for loop thus parallel stream are processing it one by one and providing the correct order.
* @param startInclusive the (inclusive) initial value
* @param endInclusive the inclusive upper bound
* @return a sequential {@code IntStream} for the range of {@code int}
* elements
*/
public static IntStream rangeClosed(int startInclusive, int endInclusive) {
Case 1 - It is obvious that the list will be processed in parallel thus the order will not be correct. Since mapping operation is performed in parallel, the results for the same input could vary from run to run, due to thread scheduling differences thus no guarantees that different operations on the "same" element within the same stream pipeline are executed in the same thread also there is no guarantee how a mapper function is also applied to the particular elements within the stream.
Source Java Doc
edited Nov 16 '18 at 4:56
answered Nov 16 '18 at 3:49
zackzack
868
868
@Kartik: There is no guarantees that different operations on the "same" element within the same stream pipeline are executed in the same thread. So there is no guarantee how a mapper function is also applied to the particular elements within the stream or in what thread any behavioral parameter is executed.Thus result might be unexpected as well.
– zack
Nov 16 '18 at 4:20
add a comment |
@Kartik: There is no guarantees that different operations on the "same" element within the same stream pipeline are executed in the same thread. So there is no guarantee how a mapper function is also applied to the particular elements within the stream or in what thread any behavioral parameter is executed.Thus result might be unexpected as well.
– zack
Nov 16 '18 at 4:20
@Kartik: There is no guarantees that different operations on the "same" element within the same stream pipeline are executed in the same thread. So there is no guarantee how a mapper function is also applied to the particular elements within the stream or in what thread any behavioral parameter is executed.Thus result might be unexpected as well.
– zack
Nov 16 '18 at 4:20
@Kartik: There is no guarantees that different operations on the "same" element within the same stream pipeline are executed in the same thread. So there is no guarantee how a mapper function is also applied to the particular elements within the stream or in what thread any behavioral parameter is executed.Thus result might be unexpected as well.
– zack
Nov 16 '18 at 4:20
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%2f53329809%2fwhy-atomicinteger-based-stream-solutions-are-not-recommended%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
6
tldr: Side-effects are "icky", even if "thread-safe". The ordering argument is very relevant in more generalized cases: eg. instead of adding integers (x + y == y + x), what if it was concatenating strings (concat(x,y) != concat(y,x))? There is much less chance to accidentally introduce such if side-effects are avoided.
– user2864740
Nov 16 '18 at 0:42
2
It's unclear from the docs whether statelessness is required or only recommended. I personally see no problem with #1, though I can imagine #2 performing better.
– shmosel
Nov 16 '18 at 0:45
2
Streams come from functional programming, where you should ideally have no side effects. Sometimes this isn't possible, but if there is a simple way to achieve the same thing without side effects you should use that.
– Peter Lawrey
Nov 16 '18 at 8:53
4
Well, when you are fine with a result where the numbers are neither, in order nor reflecting the source element order, the only remaining issue is that it is inefficient, compared to the recommended approach. But most other Q&As are about tasks not being fine with such a wrong order and well, when you use this pattern in these rare case where you are fine with a meaningless number, it might soon become a habit…
– Holger
Nov 16 '18 at 9:05