Saturday, November 28, 2015

Java 8 - Streams (Part - I)

Stream represents a sequence of elements which can calculate or compute on-demand. Stream is an interface like an Iterator but it can do parallel processing. In other words, we can say Stream is a lazy collection where the values are computed on-demand.

How to Create

Streams are defined in java.util.stream package. Stream can be obtained using various options but a simple way is to get Stream is from Collection Interface. A default method defined as stream in Collection Interface to get it from the respective collection built. This is one of the best example to explain why the default methods are introduced in Java 8. See my previous post for more details on default methods. Collection Interface has two default methods for streams
  • stream(): Returns a stream associated with the collection for processing
  • parallelStream(): Same as stream method but returns for parallel processing. 
Stream here is the generic type. There are few streams defined for the primitive types as IntStream, DoubleStream, LongStream etc.

How to Use

As mentioned earlier - Streams are like Iterators or more than Iterators. Streams can be used to find an element, get first element, sort the elements etc. Apart from using streams on Collections, they can be used to operate on Paths, files, range of numbers etc. We will see few of them with examples

Streaming Files

BufferedReader class has got a new method lines() to return the lines in that file as Stream. See below
        try (FileReader fr = new FileReader("/tmp/sample.txt");
                BufferedReader br = new BufferedReader(fr))
        {
            br.lines().forEach(System.out::println);
        }
The above code returns the Stream which represents the sequence of lines in the file /tmp/sample.txt and, prints them. To add to it, Stream has got a method forEach to iterate over it. The Files class has also has a method lines() to read the Path as stream.
        try (Stream<String> st = Files.lines(Paths.get("/tmp/sample.txt")))
        {
            st.forEach(System.out::println);
        }

Streaming Patterns

Pattern can also return a Stream object with the matched values. Lets see that
        Pattern p = Pattern.compile("-");
        p.splitAsStream("5-13-93").forEach(System.out::println);
This will split the pattern 5-13-93 into three integers (5,13 and 93) and prints them.

Streaming Range

Stream interface has method to find range of primitive types (Int, Double or Long) as a Stream itself. We can use the respective Stream Interface (IntStream, DoubleStream and LongStream) to get the range associated with it. See - for example
IntStream.range(10, 20).forEach(System.out::println);

Stream Functions

Stream provides various methods to operate on Collections. We will discuss few of them here with examples. Most of the methods of Stream interface take lambda expression as argument (i.e. a method name - See Lambda Expressions for more details).

of:

This is a static method defined in Stream Interface to create a generic Stream. The method exists in other specific Streams like Double, Long etc to create its own type.
Stream<String> stream = Stream.of("Veeresh", "Blog", "Stream")

forEach:

The function will iterate through the Stream. All the above example has got forEach with prints to console

sort:

This method sorts the Stream elements associated with it in natural order (i.e. ascending order). See here
Stream.of("Veeresh", "Blog", "Stream").sorted().forEach(System.out::println);
The above code prints the sorted names. sort() is a very lazy function, means it doesn't effect until you call any other method after/before sort. In the above example - When the sort method was executed nothing changed on the Stream. Once the foreach method was called - the sorting was done.

Apart from these - stream has few other methods which are more powerful and useful which reduces developer effort, Increases readability and reduces LOC. We will discuss those in the next post. For now - this is it.

Happy Learning!!!!