Java Stream API CheatSheet:

satish kathiriya
5 min readSep 7, 2021

--

Java stream API has the following important methods:

Java stream is a pipeline of functions or operations. These operations can be classed into two operations.

  1. Intermediate operation
  2. Terminal operation.

The difference between the two is in the output which the operation creates. If an operation outputs another stream, to which you could apply a further operation, we call it an intermediate operation. However, if the operation outputs a concrete type or produces a side effect, it is a terminal type.

A subsequent stream operation cannot follow a terminal operation, obviously, as a stream is not returned by the terminal operation!

Intermediate Operations

An intermediate operation is always lazily executed. That is to say, they are not run until the point a terminal operation is reached. We’ll look in more depth at a few of the most popular intermediate operations used in a stream.

  • map — the map operation returns a stream of elements after they have been processed by the function passed in as a parameter. The elements before and after the mapping may have a different type, but there will be the same total number of elements.
  • filter — the filter operation returns a stream of elements that satisfy the predicate passed in as a parameter to the operation. The elements themselves before and after the filter will have the same type, however, the number of elements will likely change.
  • distinct — the distinct operation is a special case of the filter operation. Distinct returns a stream of elements such that each element is unique in the stream, based on the equals method of the elements.

Here’s a table that summarises this, including a couple of other common intermediate operations.

Terminal Operations

A terminal operation is always eagerly executed. This operation will kick off the execution of all previous lazy operations present in the stream. Terminal operations either return concrete types or produce a side effect. For instance, a reduce operation which calls the Integer::sum operation would produce an Optional, which is a concrete type. Alternatively, the forEach operation does not return a concrete type, but you are able to add a side effect such as print out each element. The collect terminal operation is a special type of reduce which takes all the elements from the stream and can produce a Set, Map or List. Here’s a tabulated summary.

STREAM EXAMPLE

Exercise 1: Get the even number from the list of string number

List<String> numbers = Arrays.asList("1", "2", "3", "4", "5", "6");//Convert list of string to even number listList<Integer> even = numbers.stream()
.map(s -> Integer.valueOf(s))
.filter(number -> number % 2 == 0)
.collect(Collectors.toList());

Exercise 2: Get the unique surnames in uppercase of the first 15 book authors that are 50 years old or older.

List<String> surname =
library.stream()
.map(book -> book.getAuthor())
.filter(author -> author.getAge() >= 50)
.map(Author::getSurname)
.map(String::toUpperCase)
.distinct()
.limit(15)
.collect(toList()));

So you know, the source of our stream, library, is an ArrayList. Check out the code and follow along with the description. From this list of books, we first need to map from books to the book authors which gets us a stream of Authors and then filter them to just get those authors that are 50 or over. We’ll map the surname of the Author, which returns us a stream of Strings. We’ll map this to uppercase Strings and make sure the elements are unique in the stream and grab the first 15. Finally we return this as a list using toList from java.util.streams.Collectors.

Exercise 3: Print out the sum of ages of all female authors younger than 25.

int sum = library.stream()
.map(Book::getAuthor)
.filter(author -> author.getGender() == Gender.FEMALE)
.map(Author::getAge)
.filter(age -> age < 25)
.reduce(0, Integer::sum)

Using the same original stream we once again map the elements from Books to Authors and filter just on those authors that are female. Next we map the elements from Authors to author ages which gives us a stream of ints. We filter ages to just those that are less than 25 and use a reduce operation and Integer::sum to total the ages.

Exercise 4: Return first employee with a salary greater than 100000 . If no such employee exists, then null is returned.

Employee employee = 
Stream.of(empIds)
.map(employeeRepository::findById)
.filter(e -> e != null)
.filter(e -> e.getSalary() > 100000)
.findFirst()
.orElse(null);

Exercise 4: Convert stream to array

Employee[] employees = empList.stream().toArray(Employee[]::new);

Boxing and Unboxing

There’re times when we need to convert primitive values to their wrapper equivalents.

In those cases, we can use the boxed() method:

List<Integer> evenInts = IntStream.rangeClosed(1, 10)
.filter(i -> i % 2 == 0)
.boxed()
.collect(Collectors.toList());

We can also convert from the wrapper class stream to the primitive stream:

// returns 78
int sum = Arrays.asList(33,45)
.stream()
.mapToInt(i -> i)
.sum();

We can always use mapToXxx and flatMapToXxx methods to create primitive streams.

Java Stream Creation

Let’s first obtain a stream from an existing array:

private static Employee[] arrayOfEmps = {
new Employee(1, "Jeff Bezos", 100000.0),
new Employee(2, "Bill Gates", 200000.0),
new Employee(3, "Mark Zuckerberg", 300000.0)
};
Stream.of(arrayOfEmps);

We can also obtain a stream from an existing list:

private static List<Employee> empList = Arrays.asList(arrayOfEmps);
empList.stream();

Note that Java 8 added a new stream() method to the Collection interface.

And we can create a stream from individual objects using Stream.of():

Stream.of(arrayOfEmps[0], arrayOfEmps[1], arrayOfEmps[2]);

Or simply using Stream.builder():

Stream.Builder<Employee> empStreamBuilder = Stream.builder();empStreamBuilder.accept(arrayOfEmps[0]);
empStreamBuilder.accept(arrayOfEmps[1]);
empStreamBuilder.accept(arrayOfEmps[2]);
Stream<Employee> empStream = empStreamBuilder.build();

--

--