Streams 1
Streams 1
The addition of the Stream was one of the major features added to Java 8.
This in-depth tutorial is an introduction to the many functionalities
supported by streams, with a focus on simple, practical examples.
To understand this material, you need to have a basic, working
knowledge of Java 8 (lambda expressions, Optional, method references).
Introduction
First of all, Java 8 Streams should not be confused with Java I/O streams
(ex: FileInputStream etc); these have very little to do with each other.
Simply put, streams are wrappers around a data source, allowing us to
operate with that data source and making bulk processing convenient and
fast.
A stream does not store data and, in that sense, is not a data
structure. It also never modifies the underlying data source.
This functionality – java.util.stream – supports functional-style operations
on streams of elements, such as map-reduce transformations on
collections.
Let’s now dive into few simple examples of stream creation and usage –
before getting into terminology and core concepts.
Java Stream Creation
Let’s first obtain a stream from an existing array:
private static Employee[] arrayOfEmps = {
Stream.of(arrayOfEmps);
empList.stream();
empStreamBuilder.accept(arrayOfEmps[0]);
empStreamBuilder.accept(arrayOfEmps[1]);
empStreamBuilder.accept(arrayOfEmps[2]);
There are also other ways to obtain a stream, some of which we will see
in sections below.
Java Stream Operations
Let’s now see some common usages and operations we can perform on
and with the help of the stream support in the language.
forEach
forEach() is simplest and most common operation; it loops over the
stream elements, calling the supplied function on each element.
The method is so common that is has been introduced directly in Iterable,
Map etc:
@Testpublic void whenIncrementSalaryForEachEmployee_thenApplyNe
wSalary() {
assertThat(empList, contains(
hasProperty("salary", equalTo(110000.0)),
hasProperty("salary", equalTo(220000.0)),
hasProperty("salary", equalTo(330000.0))
));}
Integer[] empIds = { 1, 2, 3 };
.map(employeeRepository::findById)
.collect(Collectors.toList());
assertEquals(employees.size(), empIds.length);}
assertEquals(empList, employees);}
Integer[] empIds = { 1, 2, 3, 4 };
.map(employeeRepository::findById)
.collect(Collectors.toList());
assertEquals(Arrays.asList(arrayOfEmps[2]), employees);}
In the example above, we first filter out null references for invalid
employee ids and then again apply a filter to only keep employees with
salaries over a certain threshold.
findFirst
findFirst() returns an Optional for the first entry in the stream;
the Optional can, of course, be empty:
@Testpublic void whenFindFirst_thenGetFirstEmployeeInStream() {
Integer[] empIds = { 1, 2, 3, 4 };
.map(employeeRepository::findById)
.findFirst()
.orElse(null);
Here, the first employee with the salary greater than 100000 is returned. If
no such employee exists, then null is returned.