Practice Answers

Java Streams Workbook — Answer Key

Answer key for the Java Streams Workbook. Check these only after attempting every exercise from a blank page.

August 8, 2026·10 min read

Check these only after attempting from a blank page. If you got one wrong, note why (which rule slipped) and redo it the next day, not immediately.

→ Back to the Workbook

Assume import java.util.*; import java.util.stream.*; throughout.


Level 1 — filter, map, collect#

java
// 1.1 even numbers
nums.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());

// 1.2 squared
nums.stream().map(n -> n * n).collect(Collectors.toList());

// 1.3 word lengths
words.stream().map(String::length).collect(Collectors.toList());

// 1.4 words starting with a vowel
words.stream().filter(w -> "aeiou".indexOf(w.charAt(0)) >= 0).collect(Collectors.toList());

// 1.5 client of every order
orders.stream().map(Order::getClient).collect(Collectors.toList());

// 1.6 Eng employees' names
employees.stream().filter(e -> e.getDept().equals("Eng"))
    .map(Employee::getName).collect(Collectors.toList());

// 1.7 evens * 10
nums.stream().filter(n -> n % 2 == 0).map(n -> n * 10).collect(Collectors.toList());

// 1.8 order values
orders.stream().map(o -> o.getQuantity() * o.getPrice()).collect(Collectors.toList());

Level 2 — terminal ops & finding#

java
// 2.1
words.stream().filter(w -> w.length() > 4).count();

// 2.2
employees.stream().anyMatch(e -> e.getSalary() > 130000);

// 2.3
orders.stream().allMatch(o -> o.getQuantity() > 0);

// 2.4
words.stream().filter(w -> w.startsWith("c")).findFirst().orElse("none");

// 2.5
employees.stream().max(Comparator.comparingInt(Employee::getSalary));

// 2.6
employees.stream().min(Comparator.comparingInt(Employee::getAge))
    .map(Employee::getName).orElse("none");

// 2.7
orders.stream().mapToInt(Order::getQuantity).sum();

// 2.8
employees.stream().mapToInt(Employee::getSalary).average().orElse(0.0);

// 2.9
words.stream().mapToInt(String::length).max().orElse(0);

// 2.10
nums.forEach(System.out::println);

Level 3 — sorting, distinct, limit, skip#

java
// 3.1
nums.stream().sorted().collect(Collectors.toList());

// 3.2
nums.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());

// 3.3
orders.stream().map(Order::getStock).distinct().collect(Collectors.toList());

// 3.4
words.stream().sorted(Comparator.comparingInt(String::length)).collect(Collectors.toList());

// 3.5
words.stream()
    .sorted(Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder()))
    .collect(Collectors.toList());

// 3.6
employees.stream().sorted(Comparator.comparingInt(Employee::getSalary))
    .limit(2).map(Employee::getName).collect(Collectors.toList());

// 3.7
nums.stream().sorted().skip(3).collect(Collectors.toList());

// 3.8
employees.stream()
    .sorted(Comparator.comparing(Employee::getDept)
            .thenComparing(Comparator.comparingInt(Employee::getSalary).reversed()))
    .map(Employee::getName).collect(Collectors.toList());

// 3.9
nums.stream().sorted(Comparator.reverseOrder()).limit(3).collect(Collectors.toList());

// 3.10
words.stream().map(w -> w.charAt(0)).distinct().sorted().collect(Collectors.toList());

Level 4 — grouping & aggregation#

java
// 4.1
employees.stream().collect(Collectors.groupingBy(Employee::getDept));

// 4.2
employees.stream().collect(Collectors.groupingBy(Employee::getDept, Collectors.counting()));

// 4.3
employees.stream().collect(Collectors.groupingBy(Employee::getDept,
    Collectors.averagingInt(Employee::getSalary)));

// 4.4
orders.stream().collect(Collectors.groupingBy(Order::getClient,
    Collectors.summingInt(Order::getQuantity)));

// 4.5
employees.stream().collect(Collectors.groupingBy(Employee::getDept,
    Collectors.collectingAndThen(
        Collectors.maxBy(Comparator.comparingInt(Employee::getSalary)),
        opt -> opt.map(Employee::getSalary).orElse(0))));

// 4.6
orders.stream().collect(Collectors.groupingBy(Order::getClient,
    Collectors.mapping(Order::getStock, Collectors.joining(","))));

// 4.7
employees.stream().collect(Collectors.groupingBy(Employee::getDept,
    Collectors.mapping(Employee::getName, Collectors.toList())));

// 4.8
employees.stream().collect(Collectors.partitioningBy(e -> e.getSalary() >= 100000));

// 4.9
orders.stream().collect(Collectors.groupingBy(Order::getClient,
    Collectors.collectingAndThen(
        Collectors.maxBy(Comparator.comparingInt(Order::getQuantity)),
        opt -> opt.map(Order::getQuantity).orElse(0))));

// 4.10
orders.stream().collect(Collectors.groupingBy(Order::getStock, Collectors.counting()));

// 4.11
employees.stream().collect(Collectors.groupingBy(Employee::getDept,
    Collectors.mapping(Employee::getName,
        Collectors.collectingAndThen(Collectors.toList(),
            list -> { Collections.sort(list); return String.join(",", list); }))));

// 4.12
words.stream().collect(Collectors.groupingBy(String::length));

Level 5 — flatMap, Optional, toMap#

java
// 5.1
matrix.stream().flatMap(List::stream).collect(Collectors.toList());

// 5.2
matrix.stream().flatMap(List::stream).mapToInt(Integer::intValue).sum();
// or: matrix.stream().flatMapToInt(l -> l.stream().mapToInt(Integer::intValue)).sum();

// 5.3
sentences.stream().flatMap(s -> Arrays.stream(s.split(" "))).collect(Collectors.toList());

// 5.4
sentences.stream().flatMap(s -> Arrays.stream(s.split(" "))).distinct().count();

// 5.5
employees.stream().collect(Collectors.toMap(Employee::getName, Employee::getSalary));

// 5.6 — duplicate stock keys → keep max price
orders.stream().collect(Collectors.toMap(Order::getStock, Order::getPrice, Double::max));

// 5.7
employees.stream().map(Employee::getName)
    .max(Comparator.comparingInt(String::length)).orElse("none");

// 5.8
orders.stream().map(Order::getStock).distinct().sorted()
    .collect(Collectors.joining(", "));

// 5.9
matrix.stream().flatMap(List::stream).filter(n -> n % 2 == 0)
    .sorted().collect(Collectors.toList());

// 5.10 — toMap building lists + merge
orders.stream().collect(Collectors.toMap(
    Order::getClient,
    o -> new ArrayList<>(List.of(o.getStock())),
    (a, b) -> { a.addAll(b); return a; }));

Level 6 — advanced#

java
// 6.1
nums.stream().reduce(1, (a, b) -> a * b);

// 6.2
words.stream().reduce("", (a, b) -> a + b);

// 6.3
nums.stream().reduce(Integer::max);   // Optional<Integer>

// 6.4
employees.stream().collect(Collectors.groupingBy(Employee::getDept,
    Collectors.groupingBy(e -> e.getAge() > 35 ? "senior" : "junior")));

// 6.5
employees.stream().collect(Collectors.groupingBy(Employee::getDept,
    Collectors.summarizingInt(Employee::getSalary)));

// 6.6
employees.stream().collect(Collectors.groupingBy(Employee::getDept,
    Collectors.collectingAndThen(
        Collectors.summarizingInt(Employee::getSalary),
        s -> s.getCount() + " employees, avg " + (long) s.getAverage())));

// 6.7
orders.stream().collect(Collectors.groupingBy(Order::getClient,
    Collectors.summingDouble(o -> o.getQuantity() * o.getPrice())));

// 6.8 — teeing min & max
nums.stream().collect(Collectors.teeing(
    Collectors.minBy(Comparator.naturalOrder()),
    Collectors.maxBy(Comparator.naturalOrder()),
    (min, max) -> "min=" + min.orElse(0) + ", max=" + max.orElse(0)));

// 6.9 — two-level: client → stock → total qty
orders.stream().collect(Collectors.groupingBy(Order::getClient,
    Collectors.groupingBy(Order::getStock,
        Collectors.summingInt(Order::getQuantity))));

// 6.10 — dept with highest avg salary
employees.stream()
    .collect(Collectors.groupingBy(Employee::getDept,
        Collectors.averagingInt(Employee::getSalary)))
    .entrySet().stream()
    .max(Map.Entry.comparingByValue())
    .map(Map.Entry::getKey).orElse("none");

// 6.11 — count per (dept, decade)
employees.stream().collect(Collectors.groupingBy(Employee::getDept,
    Collectors.groupingBy(e -> (e.getAge() / 10) * 10 + "s",
        Collectors.counting())));

// 6.12 — most valuable order description
orders.stream()
    .max(Comparator.comparingDouble(o -> o.getQuantity() * o.getPrice()))
    .map(o -> o.getClient() + " " + o.getStock() + " worth "
             + (o.getQuantity() * o.getPrice()))
    .orElse("none");

Common mistakes to self-check against#

  • Put a collector inside map() instead of collect(). → Collectors always live in collect().
  • max()/min() given an identity arg (that's reduce). → They take only a comparator.
  • Flipped mapping args → it's mapping(extractFn, downstreamCollector).
  • Flipped collectingAndThen args → it's collectingAndThen(collector, finisher).
  • Forgot toMap merge function when keys can collide → runtime IllegalStateException.
  • Used map where the element expands to many → needed flatMap.
  • Forgot .orElse(...) on an Optional-returning terminal (findFirst, max, reduce one-arg).