Streams, lambdas, and functional pipelines
Transform collections declaratively. map, filter, reduce, collect — and when to fall back to a for-loop.
Runs on a sandboxed JVM via the public Piston API. Edit the code and hit Run.
Lambdas and method references
A lambda is shorthand for an anonymous implementation of a functional interface — an interface with a single abstract method. x -> x * 2, (a, b) -> a + b, and String::length are all lambdas. They capture variables from the enclosing scope but only if those variables are effectively final.
The stream pipeline
A stream is a description of a computation over a source. It has three parts: a source (collection.stream(), Stream.of(...), IntStream.range(...)), zero or more intermediate operations (map, filter, sorted, distinct), and exactly one terminal operation (collect, forEach, count, reduce). Nothing runs until the terminal operation is called — streams are lazy.
map, filter, reduce
map transforms each element. filter keeps only elements matching a predicate. reduce folds the stream to a single value with a binary operator. Together they cover most transformations you used to write with for-loops.
Runs on a sandboxed JVM via the public Piston API. Edit the code and hit Run.
Collectors
collect(Collectors.toList()) — or the shorter .toList() in Java 16+ — is the workhorse terminal. Collectors.groupingBy, partitioningBy, toMap, and joining handle almost every aggregation. toMap throws on duplicate keys unless you pass a merge function.
Primitive streams
IntStream, LongStream, and DoubleStream avoid boxing. Use mapToInt / mapToObj to move between object and primitive streams. IntStream.range(0, n) is the idiomatic replacement for a counted for-loop when you're already in a stream chain.
When not to use streams
For simple iteration with side effects, a for-each loop is clearer and often faster. Streams shine for multi-step transformations, grouping, and parallel processing. Never mutate external state from inside a stream — the contract is functional, and parallel streams will corrupt data if you break it.
Related lessons & next topics
Keep going — these pair well with Streams, lambdas, and functional pipelines.
- § 3.02 · Collections & GenericsGenerics, bounded types, and wildcards
Type-safe containers, PECS (producer-extends, consumer-super), and erasure's sharp edges.
Intermediate · 32 min - § 6.01 · Data Structures in JavaArrays and ArrayList — the workhorses
Fixed-size arrays vs dynamic ArrayList. Memory layout, amortized O(1) append, and when each one shines.
Beginner · 22 min - § 3.01 · Collections & GenericsLists, Sets, Maps — the Collections framework
ArrayList, HashMap, HashSet, and the Iterable contract. Choose the right structure for the job.
Intermediate · 30 min - § 6.02 · Data Structures in JavaLinkedList and the Deque interface
Doubly-linked nodes, O(1) insert at either end, and why LinkedList is almost never the right choice.
Beginner · 20 min