In this example,
int sum = widgets.stream() .filter(w -> w.getColor() == RED) .mapToInt(w -> w.getWeight()) .sum();
Collection<Widget>. We create a stream of
Collection.stream(), filter it to produce a stream containing only the red widgets, and then transform it into a stream of
intvalues representing the weight of each red widget. Then this stream is summed to produce a total weight.
In addition to
Stream, which is a stream of object references, there are primitive specializations for
DoubleStream, all of which are referred to as "streams" and conform to the characteristics and restrictions described here.
To perform a computation, stream operations are composed into a stream pipeline. A stream pipeline consists of a source (which might be an array, a collection, a generator function, an I/O channel, etc), zero or more intermediate operations (which transform a stream into another stream, such as
filter(Predicate)), and a terminal operation (which produces a result or side-effect, such as
forEach(Consumer)). Streams are lazy; computation on the source data is only performed when the terminal operation is initiated, and source elements are consumed only as needed.
Collections and streams, while bearing some superficial similarities, have different goals. Collections are primarily concerned with the efficient management of, and access to, their elements. By contrast, streams do not provide a means to directly access or manipulate their elements, and are instead concerned with declaratively describing their source and the computational operations which will be performed in aggregate on that source. However, if the provided stream operations do not offer the desired functionality, the
BaseStream.spliterator() operations can be used to perform a controlled traversal.
A stream pipeline, like the "widgets" example above, can be viewed as a query on the stream source. Unless the source was explicitly designed for concurrent modification (such as a
ConcurrentHashMap), unpredictable or erroneous behavior may result from modifying the stream source while it is being queried.
Most stream operations accept parameters that describe user-specified behavior, such as the lambda expression
w -> w.getWeight() passed to
mapToInt in the example above. To preserve correct behavior, these behavioral parameters:
A stream should be operated on (invoking an intermediate or terminal stream operation) only once. This rules out, for example, "forked" streams, where the same source feeds two or more pipelines, or multiple traversals of the same stream. A stream implementation may throw
IllegalStateException if it detects that the stream is being reused. However, since some stream operations may return their receiver rather than a new stream object, it may not be possible to detect reuse in all cases.
Streams have a
BaseStream.close() method and implement
AutoCloseable, but nearly all stream instances do not actually need to be closed after use. Generally, only streams whose source is an IO channel (such as those returned by
Files.lines(Path, Charset)) will require closing. Most streams are backed by collections, arrays, or generating functions, which require no special resource management. (If a stream does require closing, it can be declared as a resource in a
Stream pipelines may execute either sequentially or in parallel. This execution mode is a property of the stream. Streams are created with an initial choice of sequential or parallel execution. (For example,
Collection.stream() creates a sequential stream, and
Collection.parallelStream() creates a parallel one.) This choice of execution mode may be modified by the
BaseStream.parallel() methods, and may be queried with the