Java Streams Cheat Sheet β€” filter, map, reduce, collect, flatMap, Optional | Dataplexa

Java Streams

Stream creation  Β·  filter  Β·  map  Β·  flatMap  Β·  reduce  Β·  collect  Β·  sorted  Β·  distinct  Β·  Optional  Β·  parallel

Sheet 3 of 7 Java 8+ Intermediate Printable

Stream Pipeline β€” How It Works

source β†’ intermediate β†’ terminal
Creating a Stream
import java.util.stream.Stream;

// From a Collection
List<String> list = List.of("a","b","c");
Stream<String> s1 = list.stream();

// From varargs
Stream<Integer> s2 = Stream.of(1, 2, 3);

// From array
String[] arr = {"x", "y"};
Stream<String> s3 = Arrays.stream(arr);

// Primitive streams
IntStream    is = IntStream.range(0, 10);
LongStream   ls = LongStream.of(1L, 2L);
DoubleStream ds = DoubleStream.of(1.5, 2.5);
Pipeline Anatomy
// Source β†’ Intermediate(s) β†’ Terminal
List.of(5,3,8,1,9,2)
    .stream()              // source
    .filter(n -> n > 3)    // intermediate
    .sorted()              // intermediate
    .map(n -> n * 2)       // intermediate
    .forEach(System.out::println);
// β†’ 10  16  18

// Streams are LAZY β€” intermediate ops
// run only when terminal op is called
Intermediate vs Terminal
// INTERMEDIATE β€” return a Stream
filter(predicate)
map(function)
flatMap(function)
sorted() / sorted(comparator)
distinct()
limit(n) / skip(n)
peek(consumer)

// TERMINAL β€” end the pipeline
forEach() / collect()
reduce()  / count()
findFirst() / findAny()
anyMatch() / allMatch()
min() / max() / toList()
Streams are single-use: Once a terminal operation is called, the stream is consumed and cannot be reused. Create a new stream from the source each time.

filter()

intermediate Β· Predicate
Basic Filter
List<Integer> nums =
    List.of(1,2,3,4,5,6);

// Keep only even numbers
List<Integer> evens = nums.stream()
    .filter(n -> n % 2 == 0)
    .collect(Collectors.toList());
// [2, 4, 6]

// Chain multiple filters
nums.stream()
    .filter(n -> n > 2)
    .filter(n -> n % 2 == 0)
    .forEach(System.out::println);
// 4  6
Filter on Objects
List<String> names =
    List.of("Alice","Bob","Anna","Carol");

// Starts with "A"
names.stream()
    .filter(s -> s.startsWith("A"))
    .forEach(System.out::println);
// Alice  Anna

// Negate a predicate
Predicate<String> startsA =
    s -> s.startsWith("A");
names.stream()
    .filter(startsA.negate())  // Bob, Carol
    .forEach(System.out::println);

map()

intermediate Β· Function
Transform Elements
List<String> names =
    List.of("alice", "bob", "carol");

// String β†’ String
names.stream()
    .map(String::toUpperCase)
    .forEach(System.out::println);
// ALICE  BOB  CAROL

// String β†’ Integer
List<Integer> lengths = names.stream()
    .map(String::length)
    .collect(Collectors.toList());
// [5, 3, 5]
mapToInt / mapToLong / mapToDouble
// Use primitive streams for performance
List<String> words =
    List.of("hello", "world", "java");

int totalLen = words.stream()
    .mapToInt(String::length)
    .sum();              // 15

double avg = words.stream()
    .mapToInt(String::length)
    .average()
    .orElse(0);          // 5.0

flatMap()

intermediate Β· flatten nested streams
map vs flatMap
List<List<Integer>> nested =
    List.of(
        List.of(1,2),
        List.of(3,4),
        List.of(5,6));

// map β†’ Stream<Stream<Integer>> βœ—
nested.stream()
    .map(List::stream);   // nested!

// flatMap β†’ Stream<Integer> βœ“
nested.stream()
    .flatMap(List::stream)
    .forEach(System.out::print);
// 1 2 3 4 5 6
Split strings into words
List<String> sentences =
    List.of(
        "Hello World",
        "Java Streams");

List<String> words = sentences.stream()
    .flatMap(s -> Arrays.stream(
        s.split(" ")))
    .collect(Collectors.toList());
// ["Hello","World","Java","Streams"]
flatMapToInt β€” sum of all sublists
List<List<Integer>> groups =
    List.of(
        List.of(10,20),
        List.of(30,40));

int total = groups.stream()
    .flatMapToInt(
        l -> l.stream()
              .mapToInt(Integer::intValue))
    .sum();
// 100
Rule of thumb: Use flatMap whenever your mapping function returns a Stream, Collection, or array and you want a single flat stream instead of nested streams.

reduce()

terminal Β· aggregate
reduce() β€” 3 forms
List<Integer> nums =
    List.of(1,2,3,4,5);

// 1. With identity (no Optional)
int sum = nums.stream()
    .reduce(0, Integer::sum);  // 15

// 2. Without identity β†’ Optional
Optional<Integer> max = nums.stream()
    .reduce(Integer::max);    // Optional[5]

// Lambda form
int product = nums.stream()
    .reduce(1, (a, b) -> a * b); // 120
Prefer IntStream for numeric ops
// More efficient than reduce for numbers
IntStream.rangeClosed(1, 10)
    .sum();              // 55

IntStream.of(3,1,4,1,5)
    .average()           // OptionalDouble[2.8]
    .orElse(0);

IntStream.of(3,1,4,1,5)
    .summaryStatistics();
// count=5, sum=14, min=1, max=5, avg=2.8

collect() & Collectors

terminal Β· most powerful
Collect to Collections
import java.util.stream.Collectors;

Stream<String> s =
    Stream.of("a","b","a","c");

// β†’ List
List<String> list = s.collect(
    Collectors.toList());

// β†’ unmodifiable List (Java 16+)
List<String> imm = s.toList();

// β†’ Set (removes duplicates)
Set<String> set = s.collect(
    Collectors.toSet());

// β†’ joining strings
String joined = s.collect(
    Collectors.joining(", ","[","]"));
// [a, b, a, c]
groupingBy & partitioningBy
List<String> names =
    List.of("Alice","Bob","Anna","Carol");

// Group by first letter
Map<Character,List<String>> byLetter =
    names.stream()
    .collect(Collectors.groupingBy(
        s -> s.charAt(0)));
// {A=[Alice,Anna], B=[Bob], C=[Carol]}

// Partition into true/false
Map<Boolean,List<String>> parts =
    names.stream()
    .collect(Collectors.partitioningBy(
        s -> s.length() > 3));
// {false=[Bob], true=[Alice,Anna,Carol]}
counting, mapping, toMap
// Count per group
Map<Character,Long> counts =
    names.stream()
    .collect(Collectors.groupingBy(
        s -> s.charAt(0),
        Collectors.counting()));

// name β†’ length map
Map<String,Integer> nameLens =
    names.stream()
    .collect(Collectors.toMap(
        s -> s,
        String::length));
// {Alice=5, Bob=3, Anna=4, Carol=5}

sorted Β· distinct Β· limit Β· skip

intermediate ops
sorted()
List<Integer> nums =
    List.of(5,3,8,1);

// Natural order (ascending)
nums.stream().sorted()
    .toList();           // [1,3,5,8]

// Descending
nums.stream()
    .sorted(Comparator.reverseOrder())
    .toList();           // [8,5,3,1]

// Sort strings by length
List.of("banana","fig","apple")
    .stream()
    .sorted(Comparator.comparingInt(
        String::length))
    .toList();  // [fig, apple, banana]
distinct Β· limit Β· skip
List<Integer> data =
    List.of(1,2,2,3,3,4,5);

// Remove duplicates
data.stream().distinct().toList();
// [1, 2, 3, 4, 5]

// First 3 elements
data.stream().limit(3).toList();
// [1, 2, 2]

// Skip first 2, take next 3
data.stream().skip(2).limit(3)
    .toList();           // [2, 3, 3]

// Pagination pattern
int page = 1, size = 10;
data.stream().skip((long) page * size)
    .limit(size);

Matching & Finding

terminal Β· boolean / Optional
anyMatch Β· allMatch Β· noneMatch
List<Integer> nums =
    List.of(1,2,3,4,5);

// Is any element > 4?
nums.stream().anyMatch(n -> n > 4);   // true

// Are all elements > 0?
nums.stream().allMatch(n -> n > 0);   // true

// Are none negative?
nums.stream().noneMatch(n -> n < 0);  // true

// Count matching
long count = nums.stream()
    .filter(n -> n % 2 == 0)
    .count();              // 2
findFirst Β· findAny Β· min Β· max
// findFirst β€” returns Optional
Optional<Integer> first = nums.stream()
    .filter(n -> n > 3)
    .findFirst();   // Optional[4]

// findAny β€” better for parallel
Optional<Integer> any = nums.stream()
    .filter(n -> n > 3)
    .findAny();     // Optional[4]

// min / max
Optional<Integer> min = nums.stream()
    .min(Integer::compareTo);  // Optional[1]
Optional<Integer> max = nums.stream()
    .max(Integer::compareTo);  // Optional[5]

Optional<T>

null-safe container
Creating an Optional
// Wrap a value (nullable)
Optional<String> opt1 =
    Optional.of("hello");

// May be null β€” use ofNullable
String val = null;
Optional<String> opt2 =
    Optional.ofNullable(val);  // empty

// Explicitly empty
Optional<String> empty =
    Optional.empty();
Checking & Extracting
Optional<String> opt =
    Optional.of("hello");

opt.isPresent();          // true
opt.isEmpty();            // false (Java 11+)
opt.get();                // "hello" (throws if empty)

// Safe extraction β€” prefer these
opt.orElse("default");    // value or default
opt.orElseGet(() -> computeDefault());
opt.orElseThrow(() ->
    new RuntimeException("missing"));
Optional in pipelines
Optional<String> name =
    Optional.of("  alice  ");

// map β€” transform if present
name.map(String::trim)
    .map(String::toUpperCase)
    .orElse("unknown");  // "ALICE"

// filter β€” keep if condition met
name.filter(s -> s.length() > 3)
    .orElse("short");

// ifPresent β€” execute if value exists
name.ifPresent(System.out::println);
Never call opt.get() directly without checking isPresent() first β€” it throws NoSuchElementException on empty. Prefer orElse(), orElseGet(), or orElseThrow() always.

Parallel Streams

multi-threaded
Enable & Use Parallel Streams
// Convert to parallel
List.of(1,2,3,4,5)
    .parallelStream()
    .filter(n -> n % 2 == 0)
    .forEach(System.out::println);

// Or convert mid-pipeline
List.of(1,2,3)
    .stream()
    .parallel()        // enables parallel
    .map(n -> n * 2)
    .sequential()      // back to sequential
    .toList();
peek() β€” debug intermediate steps
List.of(1,2,3,4,5)
    .stream()
    .filter(n -> n > 2)
    .peek(n ->
        System.out.println("after filter: " + n))
    .map(n -> n * 10)
    .peek(n ->
        System.out.println("after map: " + n))
    .toList();
// Useful for debugging β€” remove in prod
Parallel caution: Parallel streams use the common ForkJoinPool. They help for large datasets with CPU-intensive, stateless operations. Avoid for I/O, small collections, or when order matters β€” the overhead can make things slower.

Method References

shorthand for lambdas
4 Types of Method References
// 1. Static method
//    Class::staticMethod
Stream.of("1","2","3")
    .map(Integer::parseInt)    // s -> Integer.parseInt(s)
    .toList();

// 2. Instance method on parameter
//    Class::instanceMethod
Stream.of("a","b")
    .map(String::toUpperCase)  // s -> s.toUpperCase()
    .toList();

// 3. Instance method on specific object
String prefix = "Hello";
Stream.of("World")
    .map(prefix::concat)       // s -> prefix.concat(s)
    .toList();

// 4. Constructor
//    Class::new
Stream.of("Alice","Bob")
    .map(StringBuilder::new)  // s -> new StringBuilder(s)
    .toList();
Use method references whenever a lambda just calls a single method β€” they're more readable and slightly faster. If the lambda has logic (n -> n * 2 + 1), keep the lambda.

Stream Operations β€” Quick Reference

all ops at a glance
Operation Type Input Returns Example
filter() IntermediatePredicate<T> Stream<T> .filter(n -> n > 0)
map() IntermediateFunction<T,R> Stream<R> .map(String::toUpperCase)
flatMap() IntermediateFunction<T,Stream<R>>Stream<R> .flatMap(List::stream)
sorted() IntermediateComparator (opt) Stream<T> .sorted(Comparator.reverseOrder())
distinct() Intermediateβ€” Stream<T> .distinct()
limit(n) Intermediatelong Stream<T> .limit(10)
skip(n) Intermediatelong Stream<T> .skip(5)
peek() IntermediateConsumer<T> Stream<T> .peek(System.out::println)
forEach() Terminal Consumer<T> void .forEach(System.out::println)
collect() Terminal Collector R .collect(Collectors.toList())
toList() Terminal β€” List<T> .toList() (Java 16+)
reduce() Terminal BinaryOperator Optional<T> / T.reduce(0, Integer::sum)
count() Terminal β€” long .count()
findFirst() Terminal β€” Optional<T> .findFirst()
anyMatch() Terminal Predicate<T> boolean .anyMatch(n -> n > 0)
allMatch() Terminal Predicate<T> boolean .allMatch(n -> n > 0)
min() / max() Terminal Comparator Optional<T> .min(Integer::compareTo)

Streams Mastery Checklist

sheet 3 complete
Pipeline SkillsKey point
Create a stream from List / array.stream() / Arrays.stream()
Explain lazy evaluationruns only at terminal op
Know intermediate vs terminalreturns Stream vs value
Avoid reusing a consumed streamcreate new stream each time
Transform & CollectKey point
filter + map + collectcore pipeline pattern
flatMap nested collectionsFunction β†’ Stream<R>
groupingBy / partitioningByMap<K, List<V>>
joining strings with delimiterCollectors.joining(", ")
Optional & SafetyKey point
Wrap nullable with ofNullablenot Optional.of(null)
Extract with orElse / orElseGetnever raw .get()
Use 4 method reference typesClass::method / obj::method
Know when to use parallellarge + CPU + stateless
Next up β†’ Sheet 4: Java OOP  Β·  classes Β· objects Β· inheritance Β· polymorphism Β· abstract classes Β· interfaces Β· encapsulation β€” the pillars of object-oriented design in Java.