Geekflare is supported by our audience. We may earn affiliate commissions from buying links on this site.
In Development Last updated: July 14, 2023
Share on:
Invicti Web Application Security Scanner – the only solution that delivers automatic verification of vulnerabilities with Proof-Based Scanning™.

A stream in Java is a sequence of elements on which sequential or parallel operations can be performed.

There can be an “n” number of intermediate operations and, at last, a terminal operation, after which the result is returned.

What is a Stream?

Streams can be managed by the Stream API, which was introduced in Java 8.

Imagine Stream as a manufacturing pipeline in which some goods need to be manufactured, sorted, and then packed for shipment. In Java, those goods are objects or collections of objects, the operations are manufacturing, sorting, and packing, and the pipeline is the stream.

The components of a stream are:

  • An initial input
  • Intermediate operations
  • Terminal operation
  • End result

Let’s explore some features of a stream in Java:

  • Stream is not an in-memory data structure; rather, it’s a sequence of arrays, objects, or collections of objects which are operated upon by certain methods.
  • Streams are declarative in nature, i.e., you specify what to do but not how to do it.
  • They can be consumed only once as they are not stored anywhere.
  • Stream doesn’t modify the original data structure; it only derives a new structure from it.
  • It returns the end result derived from the final method in the pipeline.

Stream API vs. Collections Processing

A collection is an in-memory data structure that stores and processes data. Collections provide data structures such as Set, Map, List, etc., to store data. On the other hand, a stream is a way to efficiently transfer data after processing it through a pipeline.

Here’s an example of an ArrayList collection:-

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add(0, 3);
        System.out.println(list);
    }
}

Output: 
[3]

As you can see in the above example, you can create an ArrayList collection, store data in it, and then operate on that data using different methods.

Using a stream, you can operate on an existing data structure and return a new modified value. Below is an example of creating an ArrayList collection and filtering it using a stream.

import java.util.ArrayList;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList();

        for (int i = 0; i < 20; i++) {
            list.add(i+1);
        }

        System.out.println(list);

        Stream<Integer> filtered = list.stream().filter(num -> num > 10);
        filtered.forEach(num -> System.out.println(num + " "));
    }
}

#Output

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 

In the above example, a stream is created using the existing list , and the list is iterated upon to filter values greater than 10. Notice the stream doesn’t store anything, the list is just being iterated upon, and the result is printed. If you try to print the stream, you will get a reference to the stream instead of values.

Working on Java Stream API

The Java Stream API takes in a source collection of elements or a sequence of elements and then performs operations on them to derive an end result. A stream is just a pipeline through which a sequence of elements passes and gets transformed in some way.

A stream can be created from various sources, including:

  • A collection such as a List or Set.
  • An array.
  • From files and their paths using a buffer.

There are two types of operations performed in a stream:-

  • Intermediate Operations
  • Terminal Operations

Intermediate vs. Terminal Operations

Each intermediate operation returns a new stream internally which transforms the input using the specified method. Nothing is actually traversed; Instead, it is passed on to the next stream. It is only at the terminal operation that the stream is traversed to get the desired result.

For example, you have a list of 10 numbers that you want to filter out and then map to something. Not every element of the list will be traversed immediately to get the filtered result and map it onto something else. Instead, individual elements will get checked, and if they meet the condition, they get mapped. New streams for each and every element.

The map operation will be performed on individual items which satisfy the filter and not the entire list. And at the time of the terminal operation, they are traversed and combined into a single result.

After performing the terminal operation, the stream is consumed and can no longer be used further. You must create a new stream to perform the same operations again.

working of streams in java
Source: The Bored Dev

With a surface-level understanding of how streams work, let’s jump into the implementation details of streams in Java.

#1. An Empty Stream

Create an empty stream by using the empty method of the Stream API.

import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        Stream emptyStream = Stream.empty();
        System.out.println(emptyStream.count());
    }
}

#Output
0

Here, if you print the number of elements in this stream, you get 0 as the output because it’s an empty stream with no elements. Empty streams are quite helpful in avoiding null pointer exceptions.

#2. Stream from Collections

Collections such as Lists and Set expose a stream() method that allows you to create a stream from a collection. The created stream can then be traversed to get the end result.

ArrayList<Integer> list = new ArrayList();

for (int i = 0; i < 20; i++) {
    list.add(i+1);
}

System.out.println(list);

Stream<Integer> filtered = list.stream().filter(num -> num > 10);
filtered.forEach(num -> System.out.println(num + " "));

#Output

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 

#3. Stream from Arrays

Arrays.stream() method is used for creating a stream from an array.

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        String[] stringArray = new String[]{"this", "is", "geekflare"};
        Arrays.stream(stringArray).forEach(item -> System.out.print(item + " "));
    }
}

#Output

this is geekflare 

You can also specify the starting and ending index of the elements to create a stream of. The starting index is inclusive, while the end index is exclusive.

String[] stringArray = new String[]{"this", "is", "geekflare"};
Arrays.stream(stringArray, 1, 3).forEach(item -> System.out.print(item + " "));

#Output
this is geekflare

#4. Finding min and max numbers using Streams

Accessing the maximum and minimum number of a collection or an array can be done by using Comparators in Java. The min() and max() methods accept a comparator and return an Optional object.

An Optional object is a container object which may or may not contain a non-null value. If it contains a non-null value, calling get() method on it will return the value.

import java.util.Arrays;
import java.util.Optional;

public class MinMax {
    public static void main(String[] args) {
        Integer[] numbers = new Integer[]{21, 82, 41, 9, 62, 3, 11};

        Optional<Integer> maxValue = Arrays.stream(numbers).max(Integer::compare);
        System.out.println(maxValue.get());

        Optional<Integer> minValue = Arrays.stream(numbers).min(Integer::compare);
        System.out.println(minValue.get());
    }
}

#Output
82
3

Learning Resources

Now that you have a basic understanding of Streams in Java, here are 5 resources for you to get well-versed in Java 8:

#1. Java 8 in Action

This book is a guide showcasing new features of Java 8, including streams, lambdas, and functional style programming. Quizzes and knowledge-check questions are also a part of the book, which will help you recover what you learned.

You can get this book in a paperback format as well as in the audiobook format on Amazon.

#2. Java 8 Lambdas: Functional Programming For The Masses

This book is specifically designed to teach core Java SE developers how the addition of Lambda expressions affects the Java language. It includes fluid explanations, code exercises, and examples for you to master Java 8 lambda expressions.

It is available in the paperback format and Kindle edition on Amazon.

#3. Java SE 8 for the Really Impatient

If you are an experienced Java SE developer, this book will guide you through the improvements made in Java SE 8, the stream API, the addition of lambda expressions, improvements to concurrent programming in Java, and some Java 7 features which most people don’t know about.

Preview Product Rating Price
Java SE 8 for the Really Impatient Java SE 8 for the Really Impatient $7.31

It is only available in paperback format on Amazon.

#4. Learn Java Functional Programming with Lambdas & Streams

udemy java course "Learn Java Functional Programming with Lambdas & Streams"

This course by Udemy explores the fundamentals of functional programming in Java 8 and 9. Lambda Expressions, Method References, Streams, and Functional Interfaces are the concepts this course focuses on.

It also includes a bunch of Java puzzles and exercises related to functional programming.

#5. Java Class Library

coursera - java class library

Java Class Library is a part of the Core Java Specialization offered by Coursera. It will teach you how to write type-safe code using Java Generics, understand the class library consisting of over 4000 classes, how to work with files, and handling runtime errors. However, there are some prerequisites for taking this course:

  • Introduction to Java
  • Introduction to Object-Oriented Programming with Java
  • Object-Oriented Hierarchies in Java

Final Words

The Java Stream API and the introduction of Lambda functions in Java 8 simplified and improved a lot of things in Java, such as parallel iteration, functional interfaces, less code, etc.

However, streams have some limitations; their biggest limitation is that they can only be consumed once. If you are a Java developer, the resources mentioned above can help you understand these topics in much more detail, so make sure to check them out.

You might also want to know about exception handling in Java.

  • Murtuza Surti
    Author
    Software Engineer & Content Creator
Thanks to our Sponsors
More great readings on Development
Power Your Business
Some of the tools and services to help your business grow.
  • Invicti uses the Proof-Based Scanning™ to automatically verify the identified vulnerabilities and generate actionable results within just hours.
    Try Invicti
  • Web scraping, residential proxy, proxy manager, web unlocker, search engine crawler, and all you need to collect web data.
    Try Brightdata
  • Monday.com is an all-in-one work OS to help you manage projects, tasks, work, sales, CRM, operations, workflows, and more.
    Try Monday
  • Intruder is an online vulnerability scanner that finds cyber security weaknesses in your infrastructure, to avoid costly data breaches.
    Try Intruder