There is no such thing as a rollback and under normal circumstances, there is no need for that. Stream operations are reading from a source and producing a new result. In case of an exception, there is no new result and any temporary object created during the processing will eventually get garbage collected.
In a perfect world, the terminal operation would wait for the completion of all subtasks before relaying the exception to the caller, but currently, it doesn’t, see also this Q&A. But even if it did, the subtasks continued to process items until either, reaching the end of their workload or detecting that the operation has been aborted, rather than rolling back their previous work.
Note that the documentation explicitly discourages from using functions with stateful behavior, as the results may be nondeterministic or incorrect when using a parallel stream. Even without exceptions, a parallel stream may process elements which do not contribute to the final result when performing a short-circuiting operation. In either case, those side effects produced by a function can’t be rolled back.
It must be emphasized that this also applies to the legal use of side effects, i.e. with peek
or forEach
, whose actions will not be undone in the exceptional case. If you use peek
for the intended purpose, it’s not an issue, as reporting that the element has been processed is still correct, even if the result is dropped due to a subsequent exception. If this is an issue for your action passed to forEach
, as you don’t want them to take place in the exceptional case, there is no way around collecting the elements first, e.g. via toArray
or collect(toList())
, and doing a forEach
on the result after the stream operation’s normal completion.
Of course, this is not necessary if the action only modifies the state of something that has its own rollback mechanism, like sending each element to a database.
For some cases, the streams reading operation does modify the state of the source, e.g. when reading numbers from a random number generator, lines from a BufferedReader
, or tokens from a Scanner
(Java 9). In these cases, the operation also has an impact on the source that cannot be undone.
In case of BufferedReader.lines()
and Scanner.tokens()
, the documentation explicitly states that the reader/scanner is in an unspecified state after the operation, even in the non-exceptional case, and Random
number generators are usually treated like producing unpredictable numbers anyway. So for none of these cases does the absence of a rollback cause an issue.