Java Streams API

The Java Streams API is a way to process sequences of data, such as lists, sets, or arrays, using readable pipeline operations. A stream does not store data itself; it describes steps for reading, filtering, transforming, and collecting data from a source.

What a Stream Pipeline Looks Like

A stream pipeline usually has three parts: a source, zero or more intermediate operations, and one terminal operation. The source might be a List. Intermediate operations such as filter, map, and sorted return another stream. A terminal operation such as collect, forEach, or count produces a final result or action.

Streams work well with lambda expressions and functional interfaces. For example, filter accepts a Predicate, and map accepts a Function.

Filtering and Collecting

This example starts with a list of names. It keeps only names with at least five letters, converts them to uppercase, sorts them, and collects the result into a new list.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Mia", "Noah", "Ava", "Liam", "Olivia", "Ethan");

        List<String> longNames = names.stream()
                .filter(name -> name.length() >= 5)
                .map(name -> name.toUpperCase())
                .sorted()
                .collect(Collectors.toList());

        System.out.println(longNames);
        System.out.println(names);
    }
}

Output:

[ETHAN, OLIVIA]
[Mia, Noah, Ava, Liam, Olivia, Ethan]

How the Example Works

The call to names.stream() creates a stream from the list. The filter step receives each name and keeps it only when the lambda returns true. The map step transforms each remaining name into an uppercase string. The sorted step orders the remaining values alphabetically.

The stream does not run fully until the terminal operation collect is reached. The collected list is stored in longNames. The original names list is unchanged, which is why the program prints both lists differently.

Common Stream Operations

Operation Kind Purpose
filter Intermediate Keeps values that match a condition
map Intermediate Transforms each value into another value
sorted Intermediate Sorts the stream values
distinct Intermediate Removes duplicate values
collect Terminal Builds a collection or other result
count Terminal Returns how many values are in the stream
forEach Terminal Runs an action for each value

Counting and Summing Numbers

Streams can also process numbers. The next program counts passing scores and adds bonus points to every score before summing them.

import java.util.Arrays;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Integer> scores = Arrays.asList(72, 95, 64, 88, 95);

        long passingCount = scores.stream()
                .filter(score -> score >= 70)
                .count();

        int boostedTotal = scores.stream()
                .mapToInt(score -> score + 5)
                .sum();

        System.out.println("Passing scores: " + passingCount);
        System.out.println("Boosted total: " + boostedTotal);
    }
}

Output:

Passing scores: 4
Boosted total: 439

Streams vs Loops

A normal loop is still a good choice when the code changes several variables, stops in custom ways, or needs detailed step-by-step control. A stream is often clearer when you are describing a data pipeline: choose items, transform them, sort them, and produce a result.

Do not modify the source collection inside a stream operation. Stream lambdas should usually be simple and focused on calculating a value, testing a value, or performing a final action.

Important Details

  • A stream can be used only once. Create a new stream from the source if you need another pipeline.
  • Intermediate operations are lazy; they wait until a terminal operation starts the pipeline.
  • Streams do not automatically make code faster. Use them first for clarity.
  • Use mapToInt, mapToDouble, or mapToLong when you need numeric operations such as sum or average.

Takeaway: Java streams let you express collection processing as a clear pipeline of filtering, transforming, and producing a final result.