Codectionary / Developer documentation / Java

Stream Basics (map, filter, forEach)

The Stream API, introduced in Java 8, provides a functional, declarative way to process sequences of elements from collections, arrays, or other sources. A stream pipeline consists of a source, zero or more intermediate operations (like filter and map, which are lazy and return a new stream), and exactly one terminal operation (like forEach or collect) that actually triggers processing.

Syntax

collection.stream().filter(...).map(...).collect(...)

Examples

Creating a Stream and forEach

Getting a stream from a collection and processing each element.

import java.util.List;

List<String> names = List.of("John", "Zain", "Jamal");

names.stream()
     .forEach(name -> System.out.println("Hello, " + name));

filter(): Keeping Matching Elements

Building a new stream containing only elements that satisfy a condition.

import java.util.List;

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

List<Integer> evens = numbers.stream()
    .filter(n -> n % 2 == 0)
    .toList();

System.out.println(evens);  // [2, 4, 6, 8, 10]

map(): Transforming Elements

Producing a new stream by applying a transformation to every element.

import java.util.List;

List<String> names = List.of("john", "zain", "jamal");

List<String> capitalized = names.stream()
    .map(String::toUpperCase)
    .toList();

System.out.println(capitalized);  // [JOHN, ZAIN, JAMAL]

Chaining Multiple Operations

The real power of streams: combining filter, map, and other operations into a readable pipeline.

import java.util.List;

List<String> words = List.of("apple", "hi", "banana", "ok", "cherry");

List<String> result = words.stream()
    .filter(w -> w.length() > 2)
    .map(String::toUpperCase)
    .sorted()
    .toList();

System.out.println(result);  // [APPLE, BANANA, CHERRY]

Best practices

  • Chain intermediate operations (filter, map, sorted) in the order that reduces the data as early as possible for better performance
  • Remember streams are lazy - intermediate operations do nothing until a terminal operation (like collect or forEach) is called
  • Use method references (String::toUpperCase) instead of an equivalent lambda (s -> s.toUpperCase()) when the lambda just calls one existing method
  • Remember a stream can only be consumed once - calling a terminal operation twice on the same stream throws an IllegalStateException

Runtime & versions

Java Virtual Machine. Feature availability depends on the language and runtime version used by your project; consult the official reference below.

Java Language Specification

Specifications & further reading