Stream API is introduced in JDK 1.8 to support functional-style operations on streams of elements, such as map-reduce transformations on collections. Basically it follows filter-map-reduce operational pipeline technique on streams of elements to produce results.
Why Stream?
Streams differ from collections in several ways:
- No storage. A stream is not a data structure that stores elements; instead, it conveys elements from a source such as a data structure, an array, a generator function, or an I/O channel, through a pipeline of computational operations.
- Functional in nature. An operation on a stream produces a result, but does not modify its source. For example, filtering a Stream obtained from a collection produces a new Stream without the filtered elements, rather than removing elements from the source collection.
- Laziness-seeking. Many stream operations, such as filtering, mapping, or duplicate removal, can be implemented lazily, exposing opportunities for optimization. For example, "find the first String with three consecutive vowels" need not examine all the input strings. Stream operations are divided into intermediate (Stream-producing) operations and terminal (value- or side-effect-producing) operations. Intermediate operations are always lazy.
- Possibly unbounded. While collections have a finite size, streams need not. Short-circuiting operations such as limit(n) or findFirst() can allow computations on infinite streams to complete in finite time. C
- onsumable. The elements of a stream are only visited once during the life of a stream. Like an Iterator, a new stream must be generated to revisit the same elements of the source.
Multiple ways to obtain stream:
- From a
Collection
via thestream()
andparallelStream()
methods; - From an array via
Arrays.stream(Object[])
; - From static factory methods on the stream classes, such as
Stream.of(Object[])
,IntStream.range(int, int)
orStream.iterate(Object, UnaryOperator)
; - The lines of a file can be obtained from
BufferedReader.lines()
; - Streams of file paths can be obtained from methods in
Files
; - Streams of random numbers can be obtained from
Random.ints()
; - Numerous other stream-bearing methods in the JDK, including
BitSet.stream()
,Pattern.splitAsStream(java.lang.CharSequence)
, andJarFile.stream()
.
What is stream Operations and Pipelines?
Stream operations are divided into intermediate and terminal operations and are combined to form stream pipelines. A stream pipeline consists of a source (such as a Collection
, an array, a generator function, or an I/O channel); followed by zero or more intermediate operations such as Stream.filter
or Stream.map
; and a terminal operation such as Stream.forEach
or Stream.reduce
.
- Intermediate operations:Returns a new stream. They are always lazy; executing an intermediate operation such as
filter()
does not actually perform any filtering, but instead creates a new stream that, when traversed, contains the elements of the initial stream that match the given predicate. Traversal of the pipeline source does not begin until the terminal operation of the pipeline is executed. - Terminal operations:Terminal operations, such as
Stream.forEach
orIntStream.sum
, may traverse the stream to produce a result or a side-effect. After the terminal operation is performed, the stream pipeline is considered consumed, and can no longer be used; if you need to traverse the same data source again, you must return to the data source to get a new stream. In almost all cases, terminal operations are eager, completing their traversal of the data source and processing of the pipeline before returning. Only the terminal operationsiterator()
andspliterator()
are not; these are provided as an "escape hatch" to enable arbitrary client-controlled pipeline traversals in the event that the existing operations are not sufficient to the task.
Sample pipeline Examples:
Filter-map-sum
No comments:
Post a Comment