Skip to content

Streams Utils is a set of operations written on Java 8 Streams. It allows several basic operations that are not available in the the Java 8, and that have proven to be very useful in some cases. It is an updated version of More Spliterators.

License

Notifications You must be signed in to change notification settings

forax/streams-utils

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Streams Utils

Maven Central Build Status Coverage Status

In the Java 8 Stream API, a classical stream operation can only work on the current element of the given stream, but cannot take into account the previous elements.

The right solution is to change the stream itself. Suppose we have a stream {a, b, c, d, ...} and that we need to remember the value of a when we process b. The problem can be solved by changing this stream to the following : {[a, b], [b, c], [c, d], ...}. We go from a stream of objects to a stream of pairs of objects, or maybe a stream of streams of objects.

This problem can be solved by creating a spliterator on the original stream spliterator. This is the object of this API.

The entry point of this API is meant to be the StreamsUtils factory class. Reading the Javadoc is a good idea, patterns are provided.

You can use this API directly with Maven, by adding the following dependency.

<dependency>
    <groupId>org.paumard</groupId>
    <artifactId>streams-utils</artifactId>
    <version>1.1.1</version>
</dependency>

This project was previously called More Spliterators (you can see it on my more-spliterators Github repo). Quite oddly, nobody had any clue about what is was about. So I decided to rename it to a more explicit name.

Operations provided

Cycling

Takes a stream and repeats it forever, as long as this stream has a finite size.

Grouping

Takes a stream [a, b, c, d] and returns [[a, b], [c, d]]. The grouping factor is parametrized.

Repeating

Takes a stream [a, b, c, d] and returns [a, a, b, b, c, c, d, d]. The repeating factor is parametrized.

Rolling

Takes a stream [a, b, c, d, e] and returns [[a, b], [b, c], [c, d], [d, e]]. The rolling factor is parametrized.

Traversing

Takes a set of streams and builds a stream of substreams. Each substream is made of the nth element of the corresponding stream. For instance, if we have:

stream0 = ["a00", "a01", "a02", "a03"]
stream1 = ["a10", "a11", "a12", "a13"]
stream2 = ["a20", "a21", "a22", "a23"]
stream3 = ["a30", "a31", "a32", "a33"]

The resulting stream is the following:

[["a00", "a10", "a20", "a30"],
 ["a01", "a11", "a21", "a31"],
 ["a02", "a12", "a22", "a32"],
 ["a03", "a13", "a23", "a33"]]

Validating

A validating spliterator works with a predicate and two mapping functions. When the predicate applied to the current element of the stream is true, then the first mapper is applied to that element. If it is false, then the second mapper is applied. In both cases the mapped element is added to the new spliterator.

This validating spliterator could also be implemented with a mapping function.

Interrupting

The interrupting spliterator takes an interruptor as a parameter. If this interruptor became false for a given element of the provided stream, then it ends the returned stream.

It has been created to handle the following case. We created an iterating stream on a class hierarchy:

String<Class<?>> streamOfClasses = Stream.iterate(myClass, c -> c == null ? null : c.getSuperclass());

The problem is that, once the Object.class has been met, this stream is null. We wanted to stop it. This is exactly what the interrupting spliterator does.

Stream<Class<?>> interruptedStreamOfClasses = StreamsUtils.interrupting(streamOfClasses, Objects::isNull);

The returned stream in this case will generate elements up to Object.class and will stop.

Gating

The gating spliterator does the opposite of the interrupting spliterator. Here we provide a validator. The returned stream will transmit the elements of the input stream one the provided validator has seen a valid element.

Weaving

The weaving operator is another version of the traversing operator, it could be seen as a traversing followed by a flatmap. Here is an example:

stream0 = ["a00", "a01", "a02", "a03"]
stream1 = ["a10", "a11", "a12", "a13"]
stream2 = ["a20", "a21", "a22", "a23"]
stream3 = ["a30", "a31", "a32", "a33"]

The resulting stream is the following:

["a00", "a10", "a20", "a30", "a01", "a11", "a21", "a31",
 "a02", "a12", "a22", "a32", "a03", "a13", "a23", "a33"]

Zipping

The zipping operator takes two streams and a bifunction. The resulting stream is the application of the bifunction on two elements of the streams, one at a time.

Collection on a shifting window

This method takes a Stream<E> as a parameter, the size of the window and a collector. This collector is applied on each shifting window, computes values that are returned in a stream.

Computation of averages on a shifting window

A set of methods computes such an average on a stream of objects, returning a DoubleStream with each value being the average on a given window. This average can be computed on int, long and double using the corresponding standard methods.

A first version of these methods takes a Stream<E> as a parameter, and a mapper to the corresponding primitive type. The computation first maps the stream before computing the average of the shifting window.

A second version takes a stream of primitive types as a parameter (IntStream, LongStream or DoubleStream) and does the computation directly on it. The implementation is built in such a way as no boxing / unboxing is done.

Computation of a summary statistics on a shifting window

This set of methods works the same as for the computation of averages. The difference is that a summary statistics is computed (for int, long or double) on each window. There are two versions of these methods: the first takes a Stream<E> and a mapper, the second directly a stream of primitive types.

Acknowledgements

Many thanks to Rémi Forax for his valuable advice during the development of this API.

Now Rémi you can implement FizzBuzz using the following code:

Stream<String> fizzBuzz = 
   zip(
      IntStream.range(0, 101).boxed(), 
      zip(
         cycle(Stream.of("fizz", "", "")), 
         cycle(Stream.of("buzz, "", "", "", ""))
         String::concat
      ), 
      (i, string) -> string.isEmpty() ? i.toString() : string
   );
fizzBuzz.skip(1).forEach(System.out::println);

About

Streams Utils is a set of operations written on Java 8 Streams. It allows several basic operations that are not available in the the Java 8, and that have proven to be very useful in some cases. It is an updated version of More Spliterators.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 100.0%