Java 8 Streams Tutorial

By | | Updated : 2021-03-12 | Viewed : 637 times

Java 8 Streams Tutorial

In this tutorial, we are going to learn what is the Stream how to use it and how to use Streams in Java 8. Moreover, we will earn the execution mechanism and control flow will be addressed here.

Streams in Java 8

The hardware world is advancing with a different number of features. one of the key features is the Multicore CPU. So usage of Multicore CPU is highly required to get a better result in terms of performance. Here Streams will be a very good option for the parallel execution on the bulk amount of data sources to operate it.

It might be confusing Streams with normal I/O input/out streams. But when we look deeply into the streams then it will be clear about the Steams and what does it mean.

Streams in Java 8 let allow us to convert the data source using List, Map, etc., for operation in a parallel manner or sequential manner into a resulted list without modifying the Sources. Processing data in bulk impacts the performance. Applying the Streams in this kind of bulk data operations will high performance in some cases. Conceptually Streams support the operation on collection/Arrays in a parallel/sequential manner. expressions can be used herein Streams into that Stream's operations. You can look into this article for more information.

Example code snippet for Streams

Streams Example Code Snippet
Array.List<String> monthList =
    Arrays.asList("jan", "feb", "mar", "apr", "may");

monthList
    .stream()
    .filter(s -> s.startsWith(",m"))
    .map(String::toUpperCase)
    .sorted()
    .forEach(System.out::println);

Notice that result printed on the processed list is displayed in the console. The same logic can be implemented in a normal manner. But what is befitting of using the streams is the next question. we will have a deeper look at them here.

Why Java Streams introduced?

To operate the data in bulk it is required to use collections. These operations are executed in a sequential manner hence time consumption is a bit high. Collections are a bit hassle to use in multithreading as they are not thread-safe. Due to this synchronization concept is implemented in Collection classes, manipulating the collections needs more work than usual.

How java 8 streams work?

Let\'s start by example to understand how Streams is working in Java 8.

Java 8 Streams Example
Array.List<String> monthList =
    Arrays.asList("jan", "feb", "mar", "apr", "may");

monthList
        .stream()
        .filter(s->s.endsWith("r"))
        .forEach(System.out::println);

The main them of Steams is making tasks into subtasks and executing the tasks in a parallel manner. Means code can be segregated into multiple code segments and once done with the process then all code segments result will be integrated.

Technically Speaking, Streams should use an internal mechanism as fork / joining to operate the data. As you know fork/join made subtasks from the task for processing. Once the process is complete for subtask then it will be consolidated for providing the result back. It is required to understand in detail so we will find that in another article.

Parallel vs Sequential Streams

As you know Streams worked for creating the subtasks from tasks for processing all the subtasks in parallel. But Sequential Stream is complete the process by using a single thread and never used the multi-threading. On another side Parallel Stream will take the advantage of the usage of multithreading with multi-cores mostly five available cores. In the Sequential Stream, the performance will not be increased when compared to the collection. But it is calculable in the case of the parallel stream.

Sequential Stream can be create using stream() on collections whereas parallel Stream can be created using the parallelStream() method.

Operations in Streams

When operations strike in mind about the Streams then these operations are be considered as intermediate and terminal. So what are the intermediate and terminal operations and why do they behave differently from others.

Terminal vs Intermediate

Intermediate operations are lazy till the terminal operation\'s execution is placed on that stream. This means when we call Intermediate operations such as map() then it will not be called immediately. When a Terminal operation of a particular stream is invoked/called then all other related intermediate operations will be invoked/called.

Stream operations examples

forEach()

forEach() is a terminal operation so it will provide the result of the stream with the execution of intermediate operations if any.

forEach in Streams
//display the strings with foreach
Arrays.asList("www", ".", "docsconsole", ".", "com")
        .stream()
        .forEach(System.out::print);

collect()

collect() is another terminal operation that performs the Mutable Reduction operation. The Mutable Reduction is an operation where all the input will be processed to get the desired result in the form of the desired container such as Collector.

In other words, it processes the data to create a resulted container i.e., kind of data structure which can be used to create resulted collections.

collect() in Streams
Arrays.asList(100, 200,201, 302, 403,505)
        .stream().filter(s ->{ return s % 2 == 0;}).collect(Collectors.toList())
        .forEach(System.out::println);

Here we are filtering even numbers using a filter and then create the list for getting the resulted values into List finally we are displaying all values for the same.

findfirst()

It finds the first element of the stream if exist the first element.

findFirst() in Streams
Arrays.asList(1, 2, 3, 7, 8, 9, 10, 12)
        .stream()
        .sorted(Comparator.reverseOrder())
        .findFirst().ifPresent(System.out::println);

Here we took a integer list as elements for sorting. After sorting all the elements then findFirst() can be used to get the first element.

toArray()

It is another terminal operation where it provides all elements of the stream as an array

toArray in Streams
//convert the stream as array
Object[] strArray = Arrays.asList("eeeee", "bb", "ccc", "dddd", "a")
        .stream().toArray();
Arrays.stream(strArray).forEach(System.out::println);

faltMap()

flatMap() is another intermediate operation can be apply on streams. It is flattening the result as a single stream with an array or list. This means it is transforming the number of streams into a single to stream to provide the result. Generally speaking, it merges the streams into a single stream in the final stage of returning the result.

faltMap() in Streams
List colors1 = Stream.of(Arrays.asList("red", "blue","green"),Arrays.asList("yellow", "white","back"))
        .flatMap(List::stream)
        .collect(Collectors.toList());

System.out.println(colors1);

Here result will be printed as a single list whereas if you use a map the result is streams you can find the code snippet below.

map() in Streams
List colors = Stream.of(Arrays.asList("red", "blue","green"),Arrays.asList("yellow", "white","back"))
                .map(List::stream)
                .collect(Collectors.toList());

        System.out.println(colors);

peek()

This intermediate operation can be applied to streams. peek() can perform the action on each element of each resulted stream. This means peek() can be used for debugging of streams methods.

peek() in Streams
Stream.of("jan", "feb", "mar", "april", "may","june")
        .filter(e -> e.length() > 3)
        .peek(e -> System.out.println("Filtered value: " + e))
        .map(String::toUpperCase)
        .peek(e -> System.out.println("Mapped value: " + e))
        .collect(Collectors.toList());

Notice the result of the above program here peek() will print the result of methods filter() and map().

filter()

filter() in Streams
Arrays.asList(1, 2, 3, 7, 8, 9, 10, 12)
        .stream().filter(e -> {return e%2 != 0; })
        .forEach(System.out::println);

Here result contains the odd number from the given list.

It is another intermediate operation of streams. It provides another stream based on the condition passed to the filter() method.

map()

Another intermediate method can be used to map creation elements based on a given condition.

map() in Streams
Arrays.asList(1,2,3,4,5)
        .stream()
        .map(s -> s * 5)
        .forEach(System.out::println);

Observe the result of the above program it will print the result of 5 times table.

sorted()

Another intermediate operation is used for sorting the elements and will provide the result stream.

sorted() in Streams
//display the alphabets in sorting order
Arrays.asList("eeeee", "bb", "ccc", "dddd", "a")
        .stream().sorted()
        .forEach(System.out::println);

Here all the elements will be sorted and printed in the console.

Leave A Reply