Introduction to Java streams
Tags: java, tutorial, beginners
Published: 2019-10-23 15:55:32

---
title: Introduction to Java streams
published: true
description: 
tags: java, tutorial, beginners
cover_image: https://images.pexels.com/photos/2066207/pexels-photo-2066207.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260
canonical_url: https://mednikov.net/guide-to-streams-in-java-including-after-8/
---
Hello! Both core Java and Vavr supply _streams_ that are very handy tool and together with aforesaid Optional/Option and Try enables functional style of your applications. As usual we will start from vanilla Java - first describe what is a stream and how to build _pipelines_. After it we will dive deeper into Vavr Stream and check how it is different from what Java gives us out of the box.

## What is stream in Java?

_Streams_ were introduced in Java 8 and were updated in next releases. Documentation describes a stream as a sequence of elements supporting sequential and parallel aggregate operations. Please don't confuse the word "stream": even before 8th version, Java had __InputStream__ and __OutputStream__, but these concepts have nothing in common with the hero of this post. Java _Stream_, that was introduced in Java 8 is an implementation of _monad pattern_ - a concept that was brought from functional languages. There, monads stand for computations that are defined as a sequence of steps. 

Let have a look on a simple case that was written in a traditional manner:

```java
List<String> names = Arrays.asList("Anna", "Bob", "Carolina", "Denis", "Anna", "Jack", "Marketa", "Simon", "Anna");

for (String name: names){
	if (name.equalsIgnoreCase("Anna")){
		System.out.println(name);
	}
}
```

What we do here is that we find all Annas in our list and just print them. This is a simple operation, but, nevertheless, requires us to write a lot of code for such ridiculuous task! Take another code snippet:

```java
List<String> names; // same names as before

names.stream().filter(name->name.equalsIgnoreCase("Anna")).forEach(System.out::println);
```

Same task, but now it takes only __one string of code__. What did we do here? We built a _pipeline_: 

1. Find all names equal to _Anna_
2. Print each of them

This pipeline consists of an intermediate (__fliter()__) and terminate (__forEach()__) operations, that we will observe later in this post. 

## Create streams

Stream is a programming abstraction, so it is not equal to collection, but we create it from collection. These concepts are often mixed by developers, that start with functional Java, but we need to distinguish them. In our example before we create a stream from __List__. There are several ways to initialize streams:

### From collections

This is an easiest and most obvious one. Java's __Collection__ interface has a built-in method __stream()__ that returns a sequential Stream with this collection as its source. Take a look on a code snippet below:

```java
List<Person> people;

Stream<Person> stream = people.stream();

// do something with stream...
```

### Generating streams

If you don't have a collection of defined data, you can _generate_ data for stream. This may be useful for experementing with streams API methods. We need to provide a __Supplier__ that is used to generate a random sequence of elements. Method __generate__ returns an infinite sequential unordered stream. Here is an example:

```java
DoubleStream numbers = Stream.generate(Math::random);
```

In this case we generate a _stream_ with a random Double value. __IntStream__ and __LongStream__ also provide a special method __range__ that we also can utilize to generate a stream. Take a look on a code snippet below:

```java
IntStream integers = IntStream.range(1,20);
integers.forEach(System.out::println);

LongStream longs = LongStream.rangeClosed(1,20);
longs.forEach(System.out::println);
```

In both cases we have a range between 1 and 20, but outputs are different. This is due to the fact that __range__ and __rangeClosed__ return a range that could contains _upper limit number_ or not. __rangeClosed__ method returns a range that includes __both limits__, while __range__ excludes a second value from results.

### ofNullable

Another static method that is used to create streams is __ofNullable__. It allows us to create a stream containing a single element or empty one (in case of __null__). __NB__ this method was introduced in Java 9.

Find a code below:

```java
Person anna = null;
Stream<Person> personStream = Stream.ofNullable(anna); 
```

### of

Another worth to look method to create streams is __of__. There are two overloaded versions of this method:

* of (T element)
* of (T... elements)

In the first case,  it returns a sequential Stream containing a single element __T__. In the second one, it returns a sequential ordered stream whose elements are the specified values. __NB__ that second version uses [varargs](https://docs.oracle.com/javase/8/docs/technotes/guides/language/varargs.html) as an argument. This code snippet illustrates this method:

```java
Stream<Car> cars = Stream.of(new Car("tesla"), new Car("skoda"), new Car("toyota"), new Car("mazda"));
cars.forEach(System.out::println);

Stream<Car> skoda = Stream.of(new Car("skoda"));
skoda.forEach(System.out::println);
```

### iterate

Same as __ofNullable__, this method was introduced in Java 9. __iterate__ takes two parameters: an initial value (_seed_) and _UnaryOperator_ that produces a sequence. The method starts with the seed value and iteratively applies the given function to get the next element. Here is an example:

```java
Stream.iterate(0, i -> i + 2);
```

### Empty stream

Finally, we can always create an _empty_ stream. __NB__ we mentioned __ofNullable__ method that _can_ return an empty stream, but there is another approach to get _explicitly_ empty stream. __empty__ method returns an empty sequential Stream:

```java
Stream<Double> empty = Stream.empty();
```

## What about Builder?

We explored static methods that are used to create streams. But despite them, there is another way to do it: use Builder. __Stream.Builder__ allows the creation of streams by generating elements individually and adding them to builder without temporary collections or buffers. Let have a look on it:

```java
// 1. create builder
Stream.Builder<String> builder = Stream.builder;

// 2. create stream
Stream<String> names = builder.add("anna").add("bob")
						.add("carolina").add("david")
						.build();
```

Builder is an another approach to build streams. We initialize a __Stream.Builder__ instance and then, using __add__ method populate it with values. Finally, we transform __Builder__ to __Stream__ by __build__ method.

## Assemble a pipeline

We took a broad introduction to the subject of stream creation and observed key ways to do it. Now, as we obtained a stream instance we can asseble a _pipeline_ in order to do something useful with the stream. From technical point of view, a _pipeline_ consists of a source (Collection, generator function); followed by zero or more _intermediate_ operations and a _terminal_ operation. The graph below represents a concept of pipeline:

![](https://www.mednikov.net/wp-content/uploads/2019/10/stream-pipeline.png)

In this section we briefly explore role of intermediate and terminal operations and observe most notable of them.

### Intermediate operations

Intermediate operations return new stream and are __lazy__. Their laziness means that the actual computation on the source data is performed __only__ after the terminal operation is invoked, and source elements are consumed only as needed. We can chain multiple intermediate operations, as each returns a new __Stream__ object. Take a look on the graph below:

![](https://www.mednikov.net/wp-content/uploads/2019/10/streams-intermediate-ops.png)

Let now have a quick look on most used intermediate operations.

#### Filter

In the beginning of the post we already used this operation in order to filter a collection and find matching names. In a nutshell, it returns a new stream consisting of the elements that match the given condition. This method accepts a _predicate_ that specifies a condition. 

```java
names.stream().filter(name->name.equalsIgnoreCase("Joe"));
```

In this code we use __filter__ to find only names that match __Joe__. As a result, we will obtain a _new_ stream with only Joes.

#### Map

There are several map operations, and I decided to group them together under one header. Let start with general __map__ method. It returns a new stream consisting of the results of applying the _mapper_ function to the elements of the stream. Here is an example code:

```java
Stream.of("anna", "benjamin", "carol", "david", "eliska", "frank")
	.map(String::toUpperCase)
	.forEach(System.out::println);
```

There we also have a source data that is a list of names. We apply mapping function to transform names into UPPERCASE STRINGS. In all cases, mapper is a [Function](https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html) that accepts one argument and produces a result. There other, specific mapping operations:

* __mapToInt__ = produces an IntStream consisting of the results of applying the given mapper function
* __mapToDouble__ = produces a DoubleStream consisting of the results of applying the given mapper function
* __mapToLong__ = produces a LongStream consisting of the results of applying the given mapper function

#### Distinct

Another notable intermediate operation in Java Stream API is __distinct__. It produces a stream of _distinct_ (unique) elements from the data. From technical point of view, __distinct__ method works with __equals__ of enitites in order to avoid duplicates. For ordered streams, the selection of distinct elements is stable, while for unordered ones, Java provides no stability guarantees.

```java
List<Integer> numbers = Arrays.asList(1, 1, 2, 3, 3, 4, 5, 5); 
numbers.stream().distinct().forEach(System.out::println); 
```

That is how this method works with numbers. In your custom entites, as it was mentioned you have to override __equals__ and __hashCode__ in order to distinct unique elements. I advice you to go read about [overriding hashCode and equals](https://www.javaworld.com/article/3305792/comparing-java-objects-with-equals-and-hashcode.html) before you will do this.

#### Sort

Sorting is an another important task that we have to perform with streams. __sorted__ method is an intermediate operation that provides a stream consisting of the elements of this stream, sorted according to _natural order_. Take a look on a code snippet below:

```java
List<Integer> numbers = Arrays.asList(-9, -18, 0, 25, 4); 
numbers.stream().sorted().forEach(System.out::println); 
```

Again, this is how sorting works with numbers. With custom entites you need to implement __Comparable__, otherwise, __ClassCastException__ will be thrown when terminal operation executes. If you do not implement this [marker interface](https://dev.to/andreevich/a-bit-about-marker-interfaces-in-java-b1e) you may use an overloaded __sorted__ version that accepts __Comparator__ as an argument:

```java
Stream.of("barbora", "daria", "cristopher", "adam", "fritz")
    .sorted((s1, s2) -> {
        return s1.compareTo(s2);
    }).forEach(System.out::println);
```

#### While

These two methods were also added since Java 9 release: __dropWhile__ and __takeWhile__. Both are intermediate operations that accepts _predicate_ with condition. 

* __dropWhile__ = produces a stream consisting of the remaining elements of this stream after dropping the longest prefix of elements that match the given predicate.
* __takeWhile__ = produces a stream consisting of the longest prefix of elements taken from this stream that match the given predicate.

__NB__ both works with _ordered_ streams.

Take a look on example code snippet below:

```java
Set<Integer> numbers = Set.of(1,2,3,4,5,6,7,8);
numbers.stream()
	.takeWhile(x-> x < 5)
	.forEach(System.out::println);
```

#### Limit

Last intermediate operation that we will observe in this post is __limit__. It produces a stream consisting of the elements, limited to be no longer than specified length. This method accepts one argument - __long__ value that represents a required length.

```java
List<Integer> numbers = Arrays.asList(-9, -18, 0, 12, -5, 92, 13, 50, -75, 25, 4); 
numbers.stream().sorted().limit(5).forEach(System.out::println); 
```

### Terminal operations

The other group of operations is called _terminal operations_. Compare to intermediate operations, there is only one terminal operation that is executed on stream, because after it will be performed, the stream pipeline is consumed, and can no longer be used. Terminal operations produces some _result_, not streams:

![](https://www.mednikov.net/wp-content/uploads/2019/10/streams-terminal-ops.png)

There are several notable terminal operations that we will explore here.

#### For each

We used this operation in most examples before. This method accepts __Consumer__ function that defines an action to perform on each element of the stream. You remember, that in the beginning of the post we compared two ways of doing this task:

```java
List<String> names = Arrays.asList("Anna", "Bob", "Carolina", "Denis", "Anna", "Jack", "Marketa", "Simon", "Anna");

for (String name: names){
	if (name.equalsIgnoreCase("Anna")){
		System.out.println(name);
	}
}

names.stream().filter(name->name.equalsIgnoreCase("Anna")).forEach(System.out::println);
```

We also used here _method reference_ to make code shorter and readable. In a full way it will look like this:

```java
stream.forEach(name->System.out.println(name));
```

__NB__ that for parallel stream pipelines, this operation does not guarantee to respect the encounter order of the stream, as doing so would sacrifice the benefit of parallelism. For any given element, the action may be performed at whatever time and in whatever thread the library chooses. If the action accesses shared state, it is responsible for providing the required synchronization.

#### Collect

The previous terminal operation has no return: it consumes data, but does not provide something back. However, often we need to perform some stream operation on collection and then get _changed_ collection back. In these situations we use __collect__ method. It does a mutable reduction operation on the elements of this stream using [collector](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collector.html). 

There are two overloaded versions of __collect__ method: one returns a _single_ result, while another one returns a _collection_. Let have detailed look:

```java
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5); 
List<Integer> result = numbers.collect(Collectors.toList()); 
```

In this code snippet we use built-in _Collectors_ method to collect stream to list. There are other useful methods that Java provides to us out of the box:

* __Collectors.toMap__
* __Collectors.toSet__

#### Find

Finally there are operations that return [Optional](https://dev.to/andreevich/java-optional-vavr-option-an-essential-introduction-10cb) object. I group them together, while they are separate methods. Let list them first:

* __findAny__  
* __findFirst__ 

Both of them do not have any arguments, so you may ask a very reasonable question: __how do they actually find data?__. These methods work in combination with __filter__, that we described earlier. Take a look on example:

```java
List<String> names = Arrays.asList("anna", "barbora", "andrew", "benjamin", "carol");

Optional<String> anna = names.stream().filter(name->name.equalsIgnoreCase("anna")).findFirst();
if (anna.isPresent){
	System.out.println("Anna is here!");
} else {
	System.out.println("No Anna there");
}
``` 

Here we use __findFirst__ in combination with __filter__ to find a matching result. However, this is a very artificial example: usually we don't do this, but filter by some _pattern_:

```java
// names list

names.stream().filter(name->name.startsWith("A")).findAny().ifPresent(System.out::println);
```

In both cases we got Anna. What is a difference between these two methods? As names imply, findFirst = returns matching element __first occured__. In our case they are both Anna. findAny returns __any__ matching element, that can be first or can be not: behavior of this operation is explicitly nondeterministic; it is free to select any element in the stream.

We did a comprehensive review of Java Stream and described most notable methods every developer should know (also this is not complete list, feel free to explore [Javadoc](https://docs.oracle.com/javase/9/docs/api/java/util/stream/Stream.html)). Stream is a concept, borrowed from functional programming languages and better works with other Java functional tools, like optionals. Many developers, however, find them not enough powerful. As alternative to built-in Java tools, we can use __Vavr__ library. In previous parts of this trilogy, I already described [Option](https://www.mednikov.net/using-java-optional-vs-vavr-option/) and [Try](https://www.mednikov.net/handling-exceptions-with-try-catch-and-vavr-try/) classes. 

## Conclusion

This post explored to a subject of streams - a concept that came from functional programming languages. Technically, [Java stream](https://www.mednikov.net/streams-in-java-and-vavr/) is a sequence of elements supporting sequential and parallel aggregate operations. Java started to include Streams API as a part of JDK 8 and has improved them in subsequent releases. Also, Java streams are not only ones. Vavr library that offers additions to functional Java also supplies own streams that are a bit different from vanilla Java. During this post we observed core tasks connected to Java streams: creation, building pipelines.  Have a nice day!