Home Java A little bit about Stream API(Java 8)

A little bit about Stream API(Java 8)

by admin
Short article with usage examples Stream APIin Java8 which hopefully will help new users to learn and use the functionality.

A little bit about Stream API(Java 8)
Often Stream API in Java8 is used to work with collections, allowing you to write code in a functional style.
The convenience and simplicity of the methods have contributed to developers’ interest in this functionality since its release.

So, what is Stream API In Java8? "Package java.util.stream" – "Classes to support functional-style operations on streams of elements, such as map-reduce transformations on collections" I’ll try to give my version of the translation, it’s actually support functional-style operations on streams, such as processing and "convolution" of processed data.

«Stream operations are divided into intermediate and terminal operations, and are combined to form stream pipelines. A stream pipeline consists of a source (such as a Collection, an array, a generator function, or an I/O channel); followed by zero or more intermediate operations such as Stream.filter or Stream.map; and a terminal operation such as Stream.forEach or Stream.reduce» description from the website
Let’s try to understand this definition. The authors tell us that there are intermediate and finite operations, which are combined in the form of pipelines. Streaming conveyors contain a source (e.g. collections, etc.) followed by intermediate and final operations and examples of these. It’s worth noting here that all intermediate operations on threads are lazy (LAZY). They won’t be executed until the terminal (final) operation is called.

Another interesting feature is the presence of parallelStream() I use these features to improve performance when processing large amounts of data. Parallel threads can speed up some kinds of operations. I use this feature when I know that the collection is big enough to be processed in a "ForkJoin" way. Read more about ForkJoin read a previous article on this topic – "Java 8 in Parallel. Learning how to create subtasks and monitor their execution."

Let’s finish with the theoretical part and move on to simple examples.
This example shows how to find the maximum and minimum values of a collection.

/*** Example #1* Finding the maximum and minimum values*/ArrayList<Integer> testValues = new ArrayList();testValues.add(0, 15);testValues.add(1, 1);testValues.add(2, 2);testValues.add(3, 100);testValues.add(4, 50);Optional<Integer> maxValue = testValues.stream().max(Integer::compareTo);System.out.println("MaxValue="+maxValue);Optional<Integer> minValue = testValues.stream().min(Integer::compareTo);System.out.println("MinValue="+minValue);

Let’s complicate the example a bit and add exceptions (as null) for the maximum value in example #2.

/*** Example #2* Finding the maximum value excluding null values*/ArrayList<Integer> testValuesNull = new ArrayList();testValuesNull.add(0, null);testValuesNull.add(1, 1);testValuesNull.add(2, 2);testValuesNull.add(3, 70);testValuesNull.add(4, 50);Optional<Integer> maxValueNotNull = testValuesNull.stream().filter((p) -> p != null).max(Integer::compareTo);System.out.println("maxValueNotNull="+maxValueNotNull);

Let’s complicate the examples. Let’s create a "sports camp" collection consisting of the "Name" and "Number of days in sports camp" fields. The example of creating the class itself is below.

public class SportsCamp {private String name; //Athlete's nameprivate Integer day; //Number of days in a sports camppublic SportsCamp(String name, int day) {this.name = name;this.day = day;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getDay() {return day;}public void setDay(Integer day) {this.day = day;}}

And now examples of working with new data :

import java.util.Arrays;import java.util.Collection;public class Start {public static void main(String[] args) {Collection<SportsCamp> sport = Arrays.asList(new SportsCamp("Ivan", 5), new SportsCamp("Petr", 7), new SportsCamp("Ira", 10));/*** Example 3* Search for the name of the longest camp stay*/String name = sport.stream().max((p1, p2) -> p1.getDay().compareTo(p2.getDay())).get().getName();System.out.println("Name="+name);}}

In the example a name was found, Irina, who will be in the camp the longest.
Let’s transform the example and create a situation where we have an error, and one of the entries is null in the name.

Collection<SportsCamp> sport = Arrays.asList(new SportsCamp("Ivan", 5), new SportsCamp( null, 15), new SportsCamp("Petr", 7), new SportsCamp("Ira", 10));

In this case you will get a result equal to "Name=null".Agree that’s not what we wanted.Let’s change the collection search a little bit to the new option.

/*** Example #4*/String nameTest = sport.stream().filter((p) -> p.getName() != null).max((p1, p2) -> p1.getDay().compareTo(p2.getDay())).get().getName();

The result obtained, "Ira, " is correct.
The examples show us finding the minimum and maximum values over the collections with small additions in the form of null values exclusion.
As we said, the available methods can be divided into two large groups of intermediate operations and finite operations. Authors may call them differently, for example, a variant of the name conveyor and terminal methods is used in literature and articles. There is one constructive feature when working with methods, you can "throw" a lot of intermediate operations, calling one terminal method at the end.
In the new example, let’s add sorting and output a certain element, for example, let’s add a filter on names with "Ivan" in them and make a count of such elements (exclude null values).

/*** Example #5*/long countName = sport.stream().filter((p) -> p.getName() != null p.getName().equals("Ivan")).count();System.out.println("countName="+countName);

Adding new SportsCamp("Ivan", 17) to the collection will result in "countName=2". We found two entries.
These examples used the creation of a stream from the collection; other options are available, for example, the creation of a stream from the required values, for example Stream streamFromValues = Stream.of("test1", "test2", "test3"), other options are also possible.
As mentioned above, users have the option to use "processing" using parallelStream().
Changing the example a bit, we get a new implementation :

long countNameParallel = sport.parallelStream().filter((p) -> p.getName() != null p.getName().equals("Ivan")).count();System.out.println("countNameParallel=" + countNameParallel);

The peculiarity of this variant is in the implementation of a parallel stream. I would like to point out, that parallelStream() is justified to use on powerful servers (multi-core) for large collections. I don’t give a clear definition and exact size of collections because there are so many parameters to identify and calculate. Often only testing can show you the increase in performance.

We’ve learned a bit about simple operations, we understand the difference between pipeline and terminal operations, and we’ve tried them both. Now let’s see some examples of more complex operations, such as collect and Map, Flat and Reduce.
Let’s look again at the official documentation documentation and let’s try to implement our examples.
In the new example, let’s try to convert one collection to another, by names beginning with "I" and write it in a List.

List<SportsCamp> onlyI = sport.stream().filter(p -> p.getName() != null p.getName().startsWith("I")).collect(Collectors.toList());System.out.println("SIZE="+onlyI.size());

The result will be three. Note here that the order in which the null elements are excluded is significant.
Note that Collectors has a lot of possibilities, including displaying the average value or information with statistics. As an example, let’s try combining data like this :

String campPeople = sport.stream().filter(p -> p.getName() != null).map(SportsCamp::getName).collect(Collectors.joining(" and ", "In camp ", " rest all days."));System.out.println(campPeople);

Result : "In camp Ivan and Petr and Ivan and Ira rest all days. There are several ways to use Collectors.joining.

From Map, Flat and Reduce, we will focus on the reduce example. Map and flat-map will be discussed in the next articles.
Reduce is used to "assemble" elements, in plain language, if you want to make a new instance of an object in a thread with aggregating indicators of other elements, then reduce is good for you. There are several ways of using it. Let’s consider one of them, for example, let’s summarize the data on all the days of the sports camp.

Integer daySum = sport.stream().reduce(0, (sum, p) -> sum += p.getDay(), (sum1, sum2) -> sum1 + sum2);System.out.println("DaySize=" + daySum);

In this variant reduce takes three values, the first is an identifier, the second is an accumulator, and the third is actually a "bundle". There are several other variants as well.

This article describes only a small part of the methods. Perhaps many people will be interested in them and they will appear in new projects. Combined with lambda functionality it becomes an excellent tool for writing concise and fast-executable code. Good luck to all of you.

You may also like