Package one.util.streamex

This library provides enhancements for Java 8 Stream API. Public API contains the following classes and interfaces:

StreamEx: implements Stream and provides additional functionality for object streams.

IntStreamEx, LongStreamEx, DoubleStreamEx: implement corresponding interfaces for primitive streams and enhancing them with additional functionality.

EntryStream: implements Stream of Map.Entry objects providing specific methods for operate on keys or values independently.

Each of these classes contain a bunch of static methods to create the corresponding stream using different sources: collections, arrays, Reader, Random and so on.

IntCollector, LongCollector, DoubleCollector: specialized collectors to work efficiently with primitive streams.

MoreCollectors: utility class which provides a number of useful collectors which are absent in JDK Collectors class.

Joining: an advanced implementation of joining collector.

StreamEx.Emitter, IntStreamEx.IntEmitter, LongStreamEx.LongEmitter, DoubleStreamEx.DoubleEmitter: helper interfaces to create custom stream sources.

Stream operations and pipelines

StreamEx operations are divided into intermediate, quasi-intermediate and terminal operations, and are combined to form stream pipelines. For more information about intermediate and terminal see the Stream API documentation.

In addition, due to the API limitations, a new operation type is defined in StreamEx library which is called "quasi-intermediate". In most of the cases they behave as intermediate operations: for sequential stream there should be no visible difference between intermediate and quasi-intermediate operation. The only known difference is when handling a parallel and unordered stream status. For intermediate operation there's no difference on calling parallel() before or after any intermediate operation. For quasi-intermediate operations if you call parallel() after the operation, then previous steps will remain sequential. Similarly if you create a parallel stream, perform some intermediate operations, use quasi-intermediate operation, then call sequential(), the steps before quasi-intermediate operation may still be executed in parallel.

Also the difference appears if you have an ordered stream source, but an unordered terminal operation (or collect using the unordered collector). If you have only intermediate operations in-between, then all of them will be performed as unordered. However if you have a quasi-intermediate operation, then unordered mode is not propagated through it, so the operations prior to the quasi-intermediate operation (including the quasi-intermediate operation itself) will remain ordered.

Tail stream optimization

A few quasi-intermediate operations are tail-stream optimized (TSO) which is important when using headTail method recursively. When the TSO-compatible operation understands that it should just pass-through the rest of the stream as-is, it notifies the surrounding headTail operation, and headTail operation removes the TSO-compatible operation from the pipeline shortening the call stack. This allows writing many recursively defined operations which consume constant amount of the call stack and the heap.

Non-interference

The function is called non-interfering if it does not modify the stream source. For more information see the Stream API documentation.

Stateless behaviors

The function is called stateless if its result does not depend on any state which may be changed during the stream execution. For more information see the Stream API documentation.

Reduction operations

A reduction operation takes a sequence of input elements and combines them into a single summary result. For more information see the Stream API documentation. In addition to symmetrical reduction which requires reduction function to be associative, StreamEx library provides asymmetrical reduction methods like foldLeft and foldRight. These methods can be safely used for parallel streams, but the absence of associativity may lead to the performance drawback. Use them only if you cannot provide an associative reduction function.

Mutable reduction

A mutable reduction operation accumulates input elements into a mutable result container, such as a Collection, as it processes the elements in the stream. The mutable reduction is usually performed via collectors. See the Stream API documentation for more details.

StreamEx provides better support for mutable reduction on primitive streams. There are primitive collector classes IntCollector, LongCollector and DoubleCollector which extend Collector interface, but capable to process primitive streams in more efficient way compared to using the boxed stream.

Also StreamEx library defines a number of collectors absent in JDK. See MoreCollectors class.

Short circuiting reduction

While Stream API has some short-circuiting operations which may process only some of input elements (in particular may allow to process an infinite stream in finite time), a mutable reduction via Stream.collect(Collector) is always non short-circuiting. This method is extended in StreamEx library. A new type of collectors is introduced which is called short-circuiting collectors. If such special collector is passed to StreamEx.collect or EntryStream.collect terminal operation, then this operation becomes short-circuiting as well. If you however pass such collector to the normal Stream.collect, it will act as an ordinary non-short-circuiting collector. For example, this will process only one element from an input stream:

 Optional<Integer> result = IntStreamEx.range(100).boxed().collect(MoreCollectors.first());
 
While this will process all the elements producing the same result:
 Optional<Integer> result = IntStream.range(0, 100).boxed().collect(MoreCollectors.first());
 

Note that when short-circuiting collector is used as the downstream, to standard JDK collectors like Collectors.mapping(Function, Collector) or Collectors.partitioningBy(Predicate, Collector), the resulting collector will not be short-circuiting. Instead you can use the corresponding method from MoreCollectors class. For example, this way you can get up to two odd and even numbers from the input stream in short-circuiting manner:

 Map<Boolean, List<Integer>> map = IntStreamEx.range(0, 100).boxed()
                .collect(MoreCollectors.partitioningBy(x -> x % 2 == 0, MoreCollectors.head(2)));
 

For some operations like groupingBy it's impossible to create a short-circuiting collector even if the downstream is short-circuiting, because it's not known whether all the possible groups are already created.

Currently there's no public API to create user-defined short-circuiting collectors. Also there are no short-circuiting collectors for primitive streams.

Associativity

An operator or function op is associative if the following holds:

     (a op b) op c == a op (b op c)
 
For more information see the Stream API documentation.
Author:
Tagir Valeev