Wednesday, 10 September 2014

Stream is like a pipeline for data. Streams can be sequential or parallel. The parallel streams are designed to take advantage of the multicore hardware design. Streams allow you to write collections processing code at a higher level of abstraction.

The Stream API in Java8 uses some of the Java's most advanced features. To fully utilize and understand streams, requires a solid understanding of Generics and Lambda expressions, and also basic concepts of parallelism and working knowledge of the collection framework is needed.

A Stream operates on a data source, such as an array or a collection. Streams allow a collection to send its values out one at a time, through pipeline like mechanism, where they can be processed in various ways with varying degree of parallelism.

Instead of calling parallel() method on existing stream, you can directly create parallel stream on collection object as shown below.

Stream< String > parllelSteam = strList.parallelStream();

Creating A Stream From Array Object :

You can create a stream from an array by use of static stream() method available in Arrays class, or of() method of Stream. of() method is static method in Stream interface.

consider you have an array of String,

String[] strArray = {"www","speakingcs","com"};

now lets create a Stream object from this, using of() method

Stream< String > arrStream = Stream.of(strArray);

The another way to create is calling stream() method of Arrays class and passing array reference to it. here is the code.

Stream< String > arrStream = Arrays.stream(strArray);

Applying Operations On Streams :

Operations that can be applied on Streams are of two types.
1. Intermediate Operations.
2. Terminal Operations.

Intermediate Operations :

Intermediate operations produce another stream. So, these can be used to create a pipeline that performs a sequence of actions.

These operations don't execute immediately, once the user performs terminal operation, then the intermediate operations evaluated in the order specified and the terminal operation is executed on the new stream produced by intermediate operations.

This mechanism is called as lazy behavior, and intermediate operations are treated as lazy. The use of lazy behavior enables the stream API to perform efficiently.

methods that perform intermediate operations are filter(), flatMap(), map(), limit() etc. Let's see a simple example, in this i am going to use filter() method. filter() takes Predicate as an argument and produces a stream of objects which satisfies the condition provided in the predicate.

in the above code, we are producing a stream of String objects, and filtering based upon a condition i.e strings whose length should be greater or equals to 3. Here filter() method performs intermediate operation. like this you can pipeline as many intermediate operations as you require.

Terminal Operations On Streams :

A terminal operation on stream, consumes the stream and produces a result. Once the stream is consumed, it cannot be reused. count(), forEach(), max(), min(), toArray() , these are some methods of this category. lets see an example.

long count = strList.stream().filter(s1 -> s1.length() >= 3).count();

Calling terminal operation on the consumed Stream results run time error "java.lang.IllegalStateException: stream has already been operated upon or closed".