Stream API in Java

Naveen AnamaneniUpdated · 5 min read

The Stream API in Java, in the java.util.stream package, is used to process a sequence of elements such as a list of names or numbers. A stream lets you describe what you want, such as "keep the students who passed, take their names, sort them", as a chain of operations called a pipeline, instead of writing loops that say how to do it step by step. Streams were added in Java 8 and are now the standard way to filter, transform and summarize collections. In this lesson, you will learn what a stream is, its features and the parts of a pipeline.

What Is a Stream?

A stream is not a collection. It doesn't store elements. It carries elements from a source, such as a list, through a series of operations, and produces a result. Think of it as a conveyor belt in a factory: items come from a store room, pass through machines that check and change them, and arrive at a packing station.

Source, then intermediate operations, then one terminal operation

Parts of a Stream Pipeline

PartWhat it doesExamples
SourceIt is used to supply the elements.list.stream(), Arrays.stream(arr), Stream.of(...)
Intermediate operationsThey transform the stream and return a new stream.filter, map, sorted, distinct, limit
Terminal operationIt produces the result and ends the stream.toList, collect, forEach, count, sum, reduce

Features of Streams

  • No storage: a stream holds no data of its own.
  • Doesn't change the source: filtering a stream never removes anything from the original list.
  • Lazy: intermediate operations do nothing until a terminal operation runs.
  • Single use: once a terminal operation runs, the stream is used up and can't be reused.
  • Can be parallel: parallelStream() can split the work across CPU cores.

Example: A First Pipeline

Let's see an example that lists the names of students who passed, in alphabetical order.

FirstStream.java▶ Try it
import java.util.List;
 
public class FirstStream {
    record Student(String name, int marks) { }
 
    public static void main(String[] args) {
        List<Student> students = List.of(
            new Student("Srihitha", 78),
            new Student("Navika", 91),
            new Student("Anamaneni", 38),
            new Student("Advitha", 85));
 
        List<String> passed = students.stream()
            .filter(s -> s.marks() >= 50)
            .map(Student::name)
            .sorted()
            .toList();
 
        System.out.println("Passed: " + passed);
        System.out.println("Original size: " + students.size());
    }
}
Output
Passed: [Advitha, Navika, Srihitha]
Original size: 4

Explanation:

  • students.stream() is the source.
  • filter keeps students with at least 50 marks, so Anamaneni (38) is dropped.
  • map(Student::name) turns each student into a name.
  • sorted() orders the names, and toList() is the terminal operation that collects them into a new list.
  • The original list still has 4 students; streams never change their source.

Example: A Stream Can Be Used Only Once

OnceOnly.java▶ Try it
import java.util.List;
import java.util.stream.Stream;
 
public class OnceOnly {
    public static void main(String[] args) {
        Stream<String> names = List.of("Navika", "Srihitha").stream();
        System.out.println(names.count());
        try {
            names.count();
        } catch (IllegalStateException e) {
            System.out.println(e.getMessage());
        }
    }
}
Output
2
stream has already been operated upon or closed

Explanation: The first count() consumed the stream. Calling another terminal operation on the same stream throws IllegalStateException. To process the data again, create a new stream from the source.

Stream vs Collection

CollectionStream
PurposeStores dataProcesses data
ElementsKept in memoryComputed on demand
Can be changedYes (add, remove)No; it produces new results
TraversalMany timesOnce
IterationExternal (you write the loop)Internal (the stream loops for you)

Common Mistakes

Expecting filter to remove elements from the original list. Use removeIf to change the list.

Storing a stream in a variable and using it twice. Create a new stream each time.

Forgetting the terminal operation. Without it, nothing runs.

Interview Corner

What is a stream in Java, and how is it different from a collection?

A stream, from java.util.stream, is a sequence of elements that supports a pipeline of operations, such as filter, map and collect, over a source like a collection or array. Unlike a collection, it doesn't store elements, doesn't modify its source, computes elements lazily only when a terminal operation runs, can be traversed only once, and iterates internally. Collections store data; streams describe computations on data.

Practice

  1. Easy: From a list of numbers, use a stream to make a list of the squares of the numbers greater than 10.
  2. Harder: From a list of words, produce a sorted list of the distinct words with more than four letters, in upper case.

Quick check: Stream API

4 questions
1Does a stream store its elements?
2Which is a terminal operation?
3Does filter change the source list?
4What happens if you use a stream twice?

Key Takeaways

Was this tutorial helpful?

Lesson 313 of 482