Java + Spring Boot track

Lesson 14 · Part 3: Collections & Generics

Stream API

The Stream API is a tool for declaratively transforming, filtering, and aggregating collections. It changes the for loop + temporary variable code into a one-line…

Part 3: Collections & Generics3 min readFree lesson

The Stream API is a tool for declaratively transforming, filtering, and aggregating collections. It changes the for loop + temporary variable code into a one-line pipeline, making the intent clear at a glance.

Learning Objectives

  • Create a stream with stream()
  • Know the meaning of map, filter, and reduce
  • Use collect(Collectors.toList()) and Collectors.groupingBy
  • Distinguish between intermediate and final operations.

Key concepts

1) Create stream

List<Integer> xs = List.of(1, 2, 3);
Stream<Integer> s = xs.stream();
Stream<Integer> s2 = Stream.of(1, 2, 3);
IntStream s3 = IntStream.rangeClosed(1, 5);

2) Intermediate vs final operation

[ source ] -> map -> filter -> map -> [ terminal ]
\---- middle ----/ (count/collect/forEach/...)

Intermediate operations behave lazily and are not actually executed until the final operation is encountered.

3) map / filter / reduce

int squareSum = Stream.of(1, 2, 3, 4)
    .filter(n -> n % 2 == 1)
    .map(n -> n * n)
    .reduce(0, Integer::sum);
// 1*1 + 3*3 = 10

4) collect and Collectors

import static java.util.stream.Collectors.*;

List<String> upper = list.stream().map(String::toUpperCase).collect(toList());
Map<String, List<Person>> byCity = people.stream().collect(groupingBy(Person::city));

See as example

Example 1 — StreamBasics.java: Creating and outputting a stream

import java.util.List;

public class StreamBasics {
    public static void main(String[] args) {
        List<String> langs = List.of("Java", "Kotlin", "Scala", "Groovy");
        langs.stream()
             .forEach(System.out::println);

        long count = langs.stream().count();
        System.out.println("count=" + count);
    }
}

Execution result

Java
Kotlin
Scala
Groovy
count=4

Note: System.out::println is a method reference (details in Part 18).

Example 2 — MapFilter.java: Convert + Filter

import java.util.List;
import java.util.stream.Collectors;

public class MapFilter {
    record Product(String name, int price) {}

    public static void main(String[] args) {
        List<Product> products = List.of(
            new Product("Pen", 1000),
            new Product("Notebook", 3000),
            new Product("Bag", 25000),
            new Product("Cup", 5000)
        );

        List<String> expensiveNames = products.stream()
            .filter(p -> p.price() >= 3000)
            .map(Product::name)
            .collect(Collectors.toList());

        System.out.println(expensiveNames);
    }
}

Execution result

[Notebook, Bag, Cup]

Note: A call to getter can be expressed as a shorthand method reference, such as Product::name.

Example 3 — Reduce.java: Cumulative sum/maximum value

import java.util.List;
import java.util.Optional;

public class Reduce {
    public static void main(String[] args) {
        List<Integer> xs = List.of(3, 1, 4, 1, 5, 9, 2, 6);
        int sum = xs.stream().reduce(0, Integer::sum);
        Optional<Integer> max = xs.stream().reduce(Integer::max);
        System.out.println("sum=" + sum);
        System.out.println("max=" + max.orElseThrow());
    }
}

Execution result

sum=31
max=9

Note: reduce with a starting value always returns a result, but without a starting value it receives Optional compared to an empty stream.

Example 4 — GroupingBy.java : count/average by group

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class GroupingBy {
    record Student(String name, String dept, int score) {}

    public static void main(String[] args) {
        List<Student> ss = List.of(
            new Student("Alice", "CS", 85),
            new Student("Bob",   "CS", 70),
            new Student("Cathy", "MATH", 92),
            new Student("Dan",   "MATH", 65),
            new Student("Eve",   "PHY", 80)
        );

        Map<String, Double> avgByDept = ss.stream()
            .collect(Collectors.groupingBy(
                Student::dept,
                Collectors.averagingInt(Student::score)));

        avgByDept.forEach((d, a) -> System.out.printf("%s -> %.1f%n", d, a));
    }
}

Execution results (order may vary)

CS -> 77.5
MATH -> 78.5
PHY -> 80.0

Note: groupingBy is powerful due to its combination of key extraction function + (optional) downstream collector.

Common Mistakes

  1. Use the stream object twice (consume it once and you're done)
  2. Unable to terminate after calling forEach on infinite stream
  3. Using lambda with side effects → dangerous in parallel streams
  4. Collectors.toList() and .toList() (JDK 16+) are slightly different (the latter is unchanged)
  5. Forgetting to convert primitive streams such as IntStream, resulting in boxing costs

Cleanup

  • Streams are declarative data processing pipelines
  • Intermediate operations are lazy, final operations are triggered
  • Just learn map / filter / reduce to use 80%
  • Collectors is powerful when you need Group/Counter

Try it yourself

cd 03_Collection_Generic/14_Stream_API/src
javac MapFilter.java
java MapFilter

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/GroupingBy.javaJava
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

public class GroupingBy {
    record Student(String name, String dept, int score) {}

    public static void main(String[] args) {
        List<Student> ss = List.of(
            new Student("Alice", "CS", 85),
            new Student("Bob",   "CS", 70),
            new Student("Cathy", "MATH", 92),
            new Student("Dan",   "MATH", 65),
            new Student("Eve",   "PHY", 80)
        );

        Map<String, Double> avgByDept = ss.stream()
            .collect(Collectors.groupingBy(
                Student::dept,
                TreeMap::new,
                Collectors.averagingInt(Student::score)));

        avgByDept.forEach((d, a) -> System.out.printf("%s -> %.1f%n", d, a));
    }
}
src/MapFilter.javaJava
import java.util.List;
import java.util.stream.Collectors;

public class MapFilter {
    record Product(String name, int price) {}

    public static void main(String[] args) {
        List<Product> products = List.of(
            new Product("Pen", 1000),
            new Product("Notebook", 3000),
            new Product("Bag", 25000),
            new Product("Cup", 5000)
        );

        List<String> expensiveNames = products.stream()
            .filter(p -> p.price() >= 3000)
            .map(Product::name)
            .collect(Collectors.toList());

        System.out.println(expensiveNames);
    }
}
src/Reduce.javaJava
import java.util.List;
import java.util.Optional;

public class Reduce {
    public static void main(String[] args) {
        List<Integer> xs = List.of(3, 1, 4, 1, 5, 9, 2, 6);
        int sum = xs.stream().reduce(0, Integer::sum);
        Optional<Integer> max = xs.stream().reduce(Integer::max);
        System.out.println("sum=" + sum);
        System.out.println("max=" + max.orElseThrow());
    }
}
src/StreamBasics.javaJava
import java.util.List;

public class StreamBasics {
    public static void main(String[] args) {
        List<String> langs = List.of("Java", "Kotlin", "Scala", "Groovy");
        langs.stream()
             .forEach(System.out::println);

        long count = langs.stream().count();
        System.out.println("count=" + count);
    }
}

Assignment

Problem 1 — Student Score Analysis

  • File name: Homework01.java
  • Core concepts: map, filter, reduce, Collectors.groupingBy

Requirements

  • record Student(String name, String dept, int score)
  • With the following data:
    • Alice CS 85, Bob CS 70, Cathy MATH 92, Dan MATH 65, Eve PHY 80
  • Print the overall average, name of the highest scoring student, and average by department.

Expected output (department order may vary)

Overall average: 78.4
Highest Score: Cathy (92)
Average by department
CS -> 77.5
MATH -> 78.5
PHY -> 80.0

Problem 2 — Sum of Even Squares

  • File name: Homework02.java
  • Core concepts: IntStream, filter, map, sum

Requirements

  • Select even integers from 1 to 10, square them, and output the sum.

Expected output

Even sum of squares = 220

Check the correct answer

After solving it yourself, compare it with the reference solution below.

Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

Homework01.javaJava
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

/** Student score analysis.*/
public class Homework01 {
    record Student(String name, String dept, int score) {}

    public static void main(String[] args) {
        List<Student> ss = List.of(
            new Student("Alice", "CS", 85),
            new Student("Bob",   "CS", 70),
            new Student("Cathy", "MATH", 92),
            new Student("Dan",   "MATH", 65),
            new Student("Eve",   "PHY", 80)
        );

        double avg = ss.stream().mapToInt(Student::score).average().orElse(0);
        Student best = ss.stream().max(Comparator.comparingInt(Student::score)).orElseThrow();
        Map<String, Double> byDept = ss.stream()
            .collect(Collectors.groupingBy(
                Student::dept,
                TreeMap::new,
                Collectors.averagingInt(Student::score)));

        System.out.printf("Overall average: %.1f%n", avg);
        System.out.println("Highest point:" + best.name() + " (" + best.score() + ")");
        System.out.println("Average by department");
        byDept.forEach((d, a) -> System.out.printf("%s -> %.1f%n", d, a));
    }
}
Homework02.javaJava
import java.util.stream.IntStream;

/** Sum of even squares from 1 to 10.*/
public class Homework02 {
    public static void main(String[] args) {
        int sum = IntStream.rangeClosed(1, 10)
            .filter(n -> n % 2 == 0)
            .map(n -> n * n)
            .sum();
        System.out.println("Even sum of squares =" + sum);
    }
}

Learn this lesson with a mentor

Work through the Java + Spring Boot track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now