Lazy Execution – Streams
Lazy Execution
A stream pipeline does not execute until a terminal operation is invoked. In other words, its intermediate operations do not start processing until their results are needed by the terminal operation. Intermediate operations are thus lazy, in contrast to the terminal operation, which is eager and executes when it is invoked.
An intermediate operation is not performed on all elements of the stream before performing the next operation on all elements resulting from the previous stream. Rather, the intermediate operations are performed back-to-back on each element in the stream. In a sense, the loops necessary to perform each intermediate operation on all elements successively are fused into a single loop (technically called loop fusion). Thus only a single pass is required over the elements of the stream.
Example 16.3 illustrates loop fusion resulting from lazy execution of a stream pipeline at (2). The intermediate operations now include print statements to announce their actions at (3) and (4). Note that we do not advocate this practice for production code. The output shows that the elements are processed one at a time through the pipeline when the terminal operation is executed. A CD is filtered first, and if it is a pop music CD, it is mapped to its title and the terminal operation includes this title in the result list. Otherwise, the CD is discarded. When there are no more CDs in the stream, the terminal operation completes, and the stream is consumed.
Short-circuit Evaluation
The lazy nature of streams allows certain kinds of optimizations to be performed on stream operations. We have already seen an example of such an optimization that results in loop fusion of intermediate operations.
In some cases, it is not necessary to process all elements of the stream in order to produce a result (technically called short-circuit execution). For instance, the limit() intermediate operation creates a stream of a specified size, making it unnecessary to process the rest of the stream once this limit is reached. A typical example of its usage is to turn an infinite stream into a finite stream. Another example is the takeWhile() intermediate operation that short-circuits stream processing once its predicate becomes false.
Certain terminal operations (anyMatch(), allMatch(), noneMatch(), findFirst(), findAny()) are also short-circuit operations, since they do not need to process all elements of the stream in order to produce a result (p. 949).
Stateless and Stateful Operations
An stateless operation is one that can be performed on a stream element without taking into consideration the outcome of any processing done on previous elements or on any elements yet to be processed. In other words, the operation does not retain any state from processing of previous elements in order to process a new element. Rather, the operation can be performed on an element independently of how the other elements are processed.
A stateful operation is one that needs to retain state from previously processed elements in order to process a new element.
The intermediate operations distinct(), dropWhile(), limit(), skip(), sorted(), and takeWhile() are stateful operations. All other intermediate operations are stateless. Examples of stateless intermediate operations include the filter() and map() operations.
Archives
- July 2024
- June 2024
- May 2024
- March 2024
- February 2024
- January 2024
- December 2023
- October 2023
- September 2023
- May 2023
- March 2023
- January 2023
- December 2022
- November 2022
- October 2022
- September 2022
- August 2022
- July 2022
- April 2022
- March 2022
- November 2021
- October 2021
- September 2021
- July 2021
- June 2021
- March 2021
- February 2021
Calendar
M | T | W | T | F | S | S |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 |