<dependency>
<groupId>com.bloidonia</groupId>
<artifactId>groovy-stream</artifactId>
<version>0.8.0</version>
</dependency>
groovy-stream is a library to simplify the generation of lazy Iterators across a mutitude of input data types, and the construction of finite Lazy Generators from a collection of inputs.
An instance of groovy.stream.Stream
is an immutable wrapper around a chain of iterators.
Each iterator in the chain takes the previous iterator’s output as it’s input, performs some task to it and passes this to the next Iterator on request.
The head iterator of the chain is wrapped in a Stream
class which allows simple
creation of new iterators in the chain.
When a Stream
method is called to add a new step to the Stream, a new Stream is
created containing the specified iterator which in turn is set to get its input
from the head iterator of the previous Stream
.
It should be noted that Stream
(as with most if not all Iterators) is not
thread-safe in itself, and any synchronization on reads should be done by the
user should multiple read threads be required (including threads reading from
intermediate Streams).
<dependency>
<groupId>com.bloidonia</groupId>
<artifactId>groovy-stream</artifactId>
<version>0.8.0</version>
</dependency>
compile "com.bloidonia:groovy-stream:0.8.0"
@Grab( "com.bloidonia:groovy-stream:0.8.0" )
import groovy.stream.*
Jar files can be directly downloaded from the Files
tab on Bintray by
following this link,
and clicking on the version you require.
The only runtime dependency of groovy-stream
is Groovy. I track the current
Groovy version, but it should work with all versions from 2.0+.
As a simple example, lets create a Stream
representing all positive integers.
Firstly, we need to import the Stream
class:
import groovy.stream.Stream
Then, we can call the Closure form of the static Stream.from
method to generate our
infinite Stream:
def x = 1
def integers = Stream.from { x++ }
We can then create a Stream
that is fed from integers and returns a Stream
of squares:
def squares = Stream.from integers map { it * it }
And we can then take the first 5 elements from this stream with:
def first5 = squares.take( 5 ).collect()
assert first5 == [ 1, 4, 9, 16, 25 ]
It is only at the point where we call collect()
that the original integers
stream is executed five times, to produce five values which are squared in turn
and passed to the result first5
.
We can use the Stream to mimic the behaviour of a list comprehension. Lets say we want to do the following:
For all values of x
from 1 to 5 and all values of y
from 1 to 3;
return x + y
if ( x + y ) % ( x + 2 ) == 0
def s = Stream.from( x:1..5, y:1..3 )
.filter { ( x + y ) % ( x + 2 ) == 0 }
.map { x + y }
// Returns results for:
// 3 - when - x:1, y:2 as (1+2)%(1+2) == 0
// 4 - when - x:2, y:2 as (2+2)%(2+2) == 0
// 5 - when - x:3, y:2 as (3+2)%(3+2) == 0
// 6 - when - x:4, y:2 as (4+2)%(4+2) == 0
// 7 - when - x:5, y:2 as (5+2)%(5+2) == 0
assert s.collect() == [ 3, 4, 5, 6, 7 ]
To create a Stream
class you can either use one of the many static from
methods:
public static <K,V> Stream<Map<K,V>> from( Map<K,? extends Iterable<V>> map )
public static <T> Stream<T> from( Stream<T> stream )
public static <T> Stream<T> from( Iterable<T> iterable )
public static <T> Stream<T> from( Iterator<T> iterator )
public static Stream<String> from( BufferedReader reader )
public static <T> Stream<T> from( Closure<T> closure )
public static <T> Stream<T> from( T[] array )
public static Stream<Byte> from( byte[] array )
public static Stream<Character> from( char[] array )
public static Stream<Short> from( short[] array )
public static Stream<Integer> from( int[] array )
public static Stream<Long> from( long[] array )
public static Stream<Float> from( float[] array )
public static Stream<Double> from( double[] array )
public static Stream<Boolean> from( boolean[] array )
public static Stream<ZipEntry> from( ZipFile file )
public static Stream<JarEntry> from( JarFile file )
Or, you can make use of the Groovy Extension class which adds the toStream()
method to:
Closure
Iterator
Iterable<T>
BufferedReader
Map<K,? extends Iterable<V>>
Object[]
byte[]
char[]
short[]
int[]
long[]
float[]
double[]
boolean[]
ZipFile
JarFile
Once you have this single Iterator Stream
, you can then perform different operations
to create a new filtered, transformed, limited or conjoined Stream
TBD
TBD
TBD
As of version 0.8.0
, you can use Java friendly versions of all of the above Closure
taking methods to access groovy-stream
functionality from native Java.