Streams in Java made super simple with examples! — An Easy Beginner’s Guide!
Tags: java, programming, softwaredevelopment, datastructures
Published: 2023-10-01 19:17:16

Data structures such as arrays, sets, etc. are commonly used to store and manipulate data in Java. However, iterating over these data structures and performing operations on them can be cumbersome. Java 8 introduced a new feature called Streams, which allows us to process data in a declarative and functional way making it more safer and concise.

In this blog, we will discuss about the concept and working of Streams in Java with a delightful analogy that makes it super easy!

## Stream

A stream is a way of processing a sequence of elements in a functional and declarative manner. **A stream does not store the data itself, but only provides a view of the data source**. This means that the data source is not modified by the stream operations.

> Imagine Java Streams like a stream of vehicles passing through a toll gate. Each vehicle represents an element in the stream. Just as vehicles pass through a toll gate one by one, elements in a stream are processed sequentially or in parallel.


![toll](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3kvd2ayzxrwhmtuj0wnw.jpeg)

Java provides a Stream interface in the `java.util.stream` package that defines the methods for creating and manipulating streams. 

However, the Stream interface does not support `primitive` types such as `int`, `long`, and `double`. To use streams with these types, we need to use their corresponding wrapper classes `Integer`, `Long`, and `Double`.

### Creating an Empty Stream

The `Stream` interface consists of a `static` and default method `empty()` that can be used to create an empty stream.

> Creating an empty stream in Java is similar to having an empty toll lane at a toll gate. Just as an empty lane at a toll gate waits for vehicles to pass through, an empty stream in Java is ready to receive elements for processing. It serves as a placeholder, waiting to be filled with data before any operations can be performed on it.

![empty stream](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d6nj8i66jlp322xpat1j.png)

In the above code, we have created an empty stream using the default method `empty()`.

### **Creating a Stream with Collections**

In Java, we can create a `Stream` from a collection (such as a List, Set, or Map) using the **`stream()`** method. Here’s how we can create a Stream from various types of collections:

**Syntax:**

```java
collection.stream()
```

**Example Syntax -**


![stream collec](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9jz2bkb6mov5k407s690.png)

In the above code, we have created a list of `Strings` and assigned it to `myList` variable. From the list of `Strings` we have created a stream and assigned it to the `streamFromList`. Here, the stream does not occupy any memory, instead it just provides a way to access the original elements.

### Direct creation of Stream

In Java, the **`Stream.of()`** method is a convenient way to create a Stream from a sequence of elements. It allows us to create a Stream directly from individual elements or an array of elements without needing a collection.

Here’s how you can use the **`Stream.of()`** method:

```java
Stream.of(list of elements);
```

**Example Syntax -**


![Streamof](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cweusbjcwgobukfae1o0.png)


## How to work with Streams?

Working with Streams involves the usage of some operations. These operations are basically divided as **Intermediate Operations** and **Terminal Operations**. Let us understand these terms easily using the same analogy of a Toll Booth.

## Intermediate Operations

> Intermediate operations are like toll booth workers who filter, transform, or manipulate the vehicles before they leave the toll gate. 

These intermediate operations **always produces a new `stream` as output** after performing the specified operations.


![toll2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fvorq74ofph5qnqp90dn.jpeg)

There are different types of intermediate operations:

- `filter()`
- `map()` 
- `sorted()` 
- `distinct()` 

### Filter()
Filtering allows us to select elements based on a certain condition.

![filter](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qfyss7f2r54ggbag8toz.png)

**Output -**

```java
Bike
Truck
Cycle
```

In the above code, we have created a stream of vehicles, filtered the names that have more than 3 letters and displayed the resultant names on the console.

### Map()
Mapping helps to perform an operation on each element of the stream.

![Image map](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3acinkgpeosaus8lanqn.png)

**Output -** 

```java
15
25
35
45
```

In the above code, the lambda expression `eachNumber -> eachNumber + 5` is being used to map each element and increment each number by 5.

### Sorted()
Sorting arranges elements in a specified order.

![Image sorted](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ifz5v3jm9ex7joxbll3.png)

Output - 

```java
100
500
700
800
```

In the above code, the `sorted()` method has arranged the elements of the numbers stream in ascending order.

### Distinct()
Distinct method removes the duplicate elements from the stream.


![Image distinct](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ww633ldf2kxoh1rgn1ql.png)

**Output -**

```java
Apple
Mango
```

In the above code, the `distinct()` method has removed the duplicate strings from the stream.

## Terminal Operations

> Terminal operations are like the toll booth itself - once a vehicle passes the toll booth, it can't go back. Similarly, terminal operations are the final actions on a stream.

A terminal operation is a stream operation that **consumes the elements of a stream and produces a result.**


![Image toll](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/es2nc94rwpdel9yg5px7.jpeg)

Types of terminal operations include:

- `forEach` 
- `reduce`
- `count` 

### ForEach()
This method is used for performing an action on each element.


![Image foreach](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tdeel9387crbmpu2qu73.png)

**Output -**

```java
Apple
Mango
Banana
Kiwi
```

In the above code,  the `forEach()` method is used to display the elements of the resultant stream on the console.

### reduce()

The **`reduce()`** is a terminal operation that combines all elements of the stream into a single result. It takes two parameters: an **identity** value and a **Binary Operator** (a function that takes two elements and produces a single result). The **`reduce()`** operation repeatedly applies the Binary Operator to the current result and each element of the stream until all elements have been processed, producing a final result.

Let’s take an example to find the sum of all numbers in a list.


![Image reduce](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g8pkxhvu95slqbdueeak.png)

**Output -**

```java
15
```

In this example, the **`reduce`** operation starts with an initial value of 0 (**`identity`**). For each element in the stream, it adds the element to the current sum (**`a + b`**). The result is the sum of all elements in the stream, which is 15 in this case.

**Use Case - `reduce()`** is useful when you want to aggregate elements of the stream into a single value, such as finding the sum, maximum, minimum, or concatenating strings.

### count()

The `count()` method returns a `long` integer that indicates the number of elements present in the stream.


![Image count](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tv4gzpgh4tjeskiuh9ih.png)

Output -

```java
3
```

In the above code, the vehicles that has greater than 3 letters were filtered and the `count()` method is used to get the number of vehicles in the resultant stream.

## ForEach() vs Map()
Wondering what’s the difference between `forEach()` and `map()` methods? Though both these operations seem to be very similar, however they behave and operate differently.

The `forEach()` method works similar to the `map()`. It iterates over the elements in the stream and performs the specified operation on each element. 

However, the only difference between `forEach()` and `map()` is - 

- The `forEach()` method does not return any stream while the `map()` method returns a stream.
- **`forEach`** is used for performing actions on elements, whereas **`map`** transforms elements.


**Use Case of `map()`:** Use **`map`** when you want to transform the elements of the Stream from one form to another. For example, transforming strings to uppercase, converting objects to specific properties, or performing any other form of transformation.


**Use Case of `forEach()`:** Use **`forEach`** when you want to perform an action (such as printing, logging, or updating external states) for each element in the Stream.

## Closing Thoughts:
Thank you for reading our blog. We appreciate your time and interest in our content. If you have any questions, feedback, or topics you’d like us to cover in our future articles, please feel free to leave a comment below. Your input is valuable, and it helps us create content that matters to you.

Stay connected with us for more insightful articles on various topics related to technology, programming, and much more.

Happy coding!