Java Streams (Java 8+)

Java Streams were introduced in Java 8 to simplify data processing on collections by providing a declarative, functional-style API.

Streams are not data structures
they are operations performed on data.


Why Streams Were Introduced

Before Java 8:

  • Data processing required loops
  • Code was verbose and error-prone
  • Parallel processing was complex

Streams solve this by providing:

  • Clean and readable code
  • Internal iteration
  • Built-in parallelism
  • Functional programming style

What Is a Stream?

A Stream is a sequence of elements from a source that supports data processing operations.

Important Clarification

  • Streams do not store data
  • Streams operate on data from a source
  • Source can be:
    • Collection
    • Array
    • I/O channel

Characteristics of Streams

  • Streams do not modify the source
  • Streams are not reusable
  • Streams support lazy evaluation
  • Streams process elements only when needed
  • Streams support parallel execution

Internal Iteration

Collections:

for (String s : list) { }

Streams:

list.stream().filter(…).map(…);

✔ Iteration handled internally
✔ Less boilerplate
✔ Safer parallel execution


Stream Pipeline Structure

A stream pipeline consists of:

  • Source
  • Intermediate operation(s)
  • Terminal operation

Source → Intermediate Ops → Terminal Op

Without a terminal operation:

  • Nothing executes.

Intermediate Operations (return Stream)

Method Input Output Use Case Example
filter Predicate Stream Select elements p -> p.getAge() > 30
map Function Stream Transform data Player::getName
flatMap 🚨 Function<T, Stream> Stream Flatten nested collections team -> team.getPlayers().stream()
sorted Comparator Stream Sort elements Comparator.comparingInt(Player::getAge)
distinct Stream Remove duplicates .distinct()
limit long Stream Get first N elements .limit(3)
skip long Stream Skip first N .skip(2)
peek ⚠️ Consumer Stream Debugging .peek(System.out::println)

Terminal Operations (end the stream)

Method Output Use Case Example
toList() List Collect results .toList()
collect() Any Custom collection/result Collectors.groupingBy(...)
forEach ⚠️ void Perform action .forEach(System.out::println)
count long Count elements .count()
anyMatch boolean Any match condition .anyMatch(p -> p.getAge() > 40)
allMatch boolean All match condition .allMatch(...)
noneMatch boolean No match .noneMatch(...)
findFirst Optional First element .findFirst()
findAny Optional Any element .findAny()
max Optional Maximum element .max(Comparator...)
min Optional Minimum element .min(Comparator...)
reduce 🚨 Optional/T Combine elements .reduce(Integer::sum)

Collectors Cheat Sheet

Basic Collectors

Method Output Use Case Example
toList() List Collect to list Collectors.toList()
toSet() Set Remove duplicates Collectors.toSet()
joining() String Join strings joining(", ")

Grouping & Aggregation

Method Output Use Case Example
groupingBy Map<K, List> Group data groupingBy(Player::getRole)
groupingBy + counting Map<K, Long> Count per group groupingBy(role, counting())
groupingBy + mapping 🚨 Map<K, List> Transform inside group mapping(Player::getName, toList())
groupingBy + maxBy Map<K, Optional> Max per group maxBy(comparator)

Advanced Collectors

Method Output Use Case Example
partitioningBy Map<Boolean, List> Split into 2 groups p -> p.getAge() > 35
collectingAndThen 🚨 Any Post-process result Optional::get
mapping 🚨 Collector Transform inside grouping mapping(name, toList())
counting Long Count elements counting()
maxBy / minBy Optional Find max/min maxBy(comparator)

Key Patterns

Problem Type Pattern
Nested list flatMap
Filter data filter
Transform data map
Group data groupingBy
Count count() or counting()
Top element max()
Boolean check anyMatch()
Top per group groupingBy + maxBy

Table of contents


This site uses Just the Docs, a documentation theme for Jekyll.