Dr. Vatsal Shah
Subject Material

Java · Collections · Topic 12

Integration of Collections, Lambdas and Streams

Combine collections, lambda expressions, stream pipelines, collectors, grouping, partitioning, and statistics in complete object-processing programs.

Topic progress · 1 of 12 sections

Step 1 of 12

12.1 Processing Student Objects

Learning objective

Connect a source collection to lambda-based stream operations and process domain objects.

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

public class CollectionsLambdaStreamDemo {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(10, 15, 20, 25, 30);

        List<Integer> result = numbers.stream()
                .filter(number -> number % 2 == 0)
                .map(number -> number * number)
                .sorted()
                .collect(Collectors.toList());

        System.out.println(result);
    }
}
Output
[100, 400, 900]
How the pieces work together
PieceResponsibility in the pipeline
ListStores the source values
stream()Creates the stream
filter() lambdaSelects even numbers
map() lambdaCalculates squares
sorted()Arranges the results
collect()Creates the final result list
StudentProcessingDemo.java
import java.util.Arrays;
import java.util.List;

public class StudentProcessingDemo {
    static class Student {
        int enrollmentNumber;
        String name;
        double marks;

        Student(int enrollmentNumber, String name, double marks) {
            this.enrollmentNumber = enrollmentNumber;
            this.name = name;
            this.marks = marks;
        }

        public String toString() {
            return enrollmentNumber + " " + name + " " + marks;
        }
    }

    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
                new Student(101, "Amit", 78),
                new Student(102, "Neha", 92),
                new Student(103, "Raj", 65),
                new Student(104, "Riya", 88));

        students.stream().forEach(System.out::println);
    }
}
Output
101 Amit 78.0
102 Neha 92.0
103 Raj 65.0
104 Riya 88.0
Check your understanding: What operation converts a collection into a sequential stream?

stream().

Step 2 of 12

12.2 Filtering Objects

Learning objective

Filter domain objects with a lambda predicate.

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

public class StudentFilterDemo {
    static class Student {
        String name; double marks;
        Student(String name, double marks) { this.name = name; this.marks = marks; }
    }

    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
                new Student("Amit", 78), new Student("Neha", 92),
                new Student("Raj", 65), new Student("Riya", 88));

        List<String> eligible = students.stream()
                .filter(student -> student.marks >= 75)
                .map(student -> student.name)
                .collect(Collectors.toList());

        System.out.println("Marks at least 75: " + eligible);
    }
}
Output
Marks at least 75: [Amit, Neha, Riya]
Check your understanding: Which students have marks of at least 75?

Amit, Neha, and Riya.

Step 3 of 12

12.3 Transforming Objects

Learning objective

Transform Student objects into another representation with map().

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

public class StudentMapDemo {
    static class Student {
        String name;
        Student(String name) { this.name = name; }
    }

    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
                new Student("Amit"), new Student("Neha"),
                new Student("Raj"), new Student("Riya"));

        List<String> names = students.stream()
                .map(student -> student.name)
                .collect(Collectors.toList());

        System.out.println(names);
    }
}
Output
[Amit, Neha, Raj, Riya]
Check your understanding: What is the output element type after mapping each student to student.name?

String.

Step 4 of 12

12.4 Sorting Objects

Learning objective

Sort objects by a numeric field in ascending and descending order.

StudentSortDemo.java
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class StudentSortDemo {
    static class Student {
        String name; double marks;
        Student(String name, double marks) { this.name = name; this.marks = marks; }
        public String toString() { return name + "=" + marks; }
    }

    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
                new Student("Amit", 78), new Student("Neha", 92),
                new Student("Raj", 65), new Student("Riya", 88));

        List<Student> ascending = students.stream()
                .sorted(Comparator.comparingDouble(student -> student.marks))
                .collect(Collectors.toList());

        List<Student> descending = students.stream()
                .sorted(Comparator.comparingDouble((Student student) -> student.marks).reversed())
                .collect(Collectors.toList());

        System.out.println("Ascending: " + ascending);
        System.out.println("Descending: " + descending);
    }
}
Output
Ascending: [Raj=65.0, Amit=78.0, Riya=88.0, Neha=92.0]
Descending: [Neha=92.0, Riya=88.0, Amit=78.0, Raj=65.0]
Check your understanding: Which method changes an ascending comparator to descending order?

reversed().

Step 5 of 12

12.5 Filtering, Sorting and Mapping Together

Learning objective

Build a multi-stage object-processing pipeline.

StudentPipelineDemo.java
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class StudentPipelineDemo {
    static class Student {
        String name; double marks;
        Student(String name, double marks) { this.name = name; this.marks = marks; }
    }

    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
                new Student("Amit", 78), new Student("Neha", 92),
                new Student("Raj", 65), new Student("Riya", 88));

        List<String> result = students.stream()
                .filter(student -> student.marks >= 75)
                .sorted(Comparator.comparingDouble((Student student) -> student.marks).reversed())
                .map(student -> student.name.toUpperCase())
                .collect(Collectors.toList());

        System.out.println(result);
    }
}
Output
[NEHA, RIYA, AMIT]

Pipeline story

  • Select students with marks of at least 75
  • Sort selected students by marks in descending order
  • Convert each selected name to uppercase
  • Collect the names into a List
Check your understanding: What is the order of operations in this example?

Filter, sort descending, map to uppercase names, then collect.

Step 6 of 12

12.6 Converting a List into a Map

Learning objective

Collect objects into key-value pairs with Collectors.toMap().

StudentToMapDemo.java
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class StudentToMapDemo {
    static class Student {
        int enrollmentNumber; String name;
        Student(int enrollmentNumber, String name) {
            this.enrollmentNumber = enrollmentNumber; this.name = name;
        }
    }

    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
                new Student(101, "Amit"), new Student(102, "Neha"),
                new Student(103, "Raj"), new Student(104, "Riya"));

        Map<Integer, String> studentMap = students.stream()
                .collect(Collectors.toMap(
                        student -> student.enrollmentNumber,
                        student -> student.name,
                        (existing, replacement) -> existing,
                        LinkedHashMap::new));

        System.out.println(studentMap);
    }
}
Output
{101=Amit, 102=Neha, 103=Raj, 104=Riya}
Check your understanding: What problem occurs when two elements produce the same map key?

A duplicate-key exception occurs unless a merge rule is provided.

Step 7 of 12

12.7 Calculating Summary Information

Learning objective

Calculate count, sum, average, minimum, and maximum in one pass.

StudentStatisticsDemo.java
import java.util.Arrays;
import java.util.DoubleSummaryStatistics;
import java.util.List;

public class StudentStatisticsDemo {
    static class Student {
        double marks;
        Student(double marks) { this.marks = marks; }
    }

    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
                new Student(78), new Student(92),
                new Student(65), new Student(88));

        DoubleSummaryStatistics statistics = students.stream()
                .mapToDouble(student -> student.marks)
                .summaryStatistics();

        System.out.println("Count: " + statistics.getCount());
        System.out.println("Total: " + statistics.getSum());
        System.out.println("Average: " + statistics.getAverage());
        System.out.println("Minimum: " + statistics.getMin());
        System.out.println("Maximum: " + statistics.getMax());
    }
}
Output
Count: 4
Total: 323.0
Average: 80.75
Minimum: 65.0
Maximum: 92.0
Check your understanding: Which method converts Student objects into a numeric stream of marks?

mapToDouble().

Step 8 of 12

12.8 Grouping Objects

Learning objective

Group objects by a classification field.

EmployeeGroupingDemo.java
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class EmployeeGroupingDemo {
    static class Employee {
        String name; String department;
        Employee(String name, String department) { this.name = name; this.department = department; }
        public String toString() { return name; }
    }

    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(
                new Employee("Amit", "IT"), new Employee("Neha", "HR"),
                new Employee("Raj", "IT"), new Employee("Riya", "Accounts"));

        Map<String, List<Employee>> departmentWise = employees.stream()
                .collect(Collectors.groupingBy(
                        employee -> employee.department,
                        LinkedHashMap::new,
                        Collectors.toList()));

        System.out.println(departmentWise);
    }
}
Output
{IT=[Amit, Raj], HR=[Neha], Accounts=[Riya]}
Grouping result
Department keyGrouped value
ITList of IT employees
HRList of HR employees
AccountsList of Accounts employees
Check your understanding: What becomes the map key in the example?

The employee department.

Step 9 of 12

12.9 Counting Grouped Objects

Learning objective

Use a downstream collector to count each group.

EmployeeCountingDemo.java
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class EmployeeCountingDemo {
    static class Employee {
        String department;
        Employee(String department) { this.department = department; }
    }

    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(
                new Employee("IT"), new Employee("HR"),
                new Employee("IT"), new Employee("Accounts"));

        Map<String, Long> departmentCount = employees.stream()
                .collect(Collectors.groupingBy(
                        employee -> employee.department,
                        LinkedHashMap::new,
                        Collectors.counting()));

        System.out.println(departmentCount);
    }
}
Output
{IT=2, HR=1, Accounts=1}
Check your understanding: What is the value type in Map<String, Long>?

Long, because Collectors.counting() returns Long counts.

Step 10 of 12

12.10 Partitioning Objects

Learning objective

Split objects into exactly two groups using a predicate.

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

public class StudentPartitionDemo {
    static class Student {
        String name; double marks;
        Student(String name, double marks) { this.name = name; this.marks = marks; }
        public String toString() { return name; }
    }

    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
                new Student("Amit", 78), new Student("Neha", 92),
                new Student("Raj", 65), new Student("Riya", 88),
                new Student("Kavya", 35));

        Map<Boolean, List<Student>> result = students.stream()
                .collect(Collectors.partitioningBy(student -> student.marks >= 40));

        System.out.println("Passed: " + result.get(true));
        System.out.println("Failed: " + result.get(false));
    }
}
Output
Passed: [Amit, Neha, Raj, Riya]
Failed: [Kavya]
Check your understanding: Which key contains students whose marks are at least 40?

true.

Step 11 of 12

12.11 Removing Duplicates and Sorting

Learning objective

Combine distinct(), sorted(), and collect() for a clean ordered result.

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

public class DistinctSortDemo {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(5, 2, 5, 1, 2, 3);

        List<Integer> result = numbers.stream()
                .distinct()
                .sorted()
                .collect(Collectors.toList());

        System.out.println(result);
    }
}
Output
[1, 2, 3, 5]

Integrated concepts

  • List supplies the data
  • Stream forms the pipeline
  • distinct() removes duplicates
  • sorted() orders values
  • collect() creates the result List
Check your understanding: What is the result for 5,2,5,1,2,3?

[1, 2, 3, 5].

Step 12 of 12

12.12 Traditional Approach versus Stream Approach

Learning objective

Compare imperative iteration with a declarative stream pipeline.

TraditionalVsStreamDemo.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class TraditionalVsStreamDemo {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(10, 15, 20, 25, 30);

        List<Integer> traditional = new ArrayList<>();
        for (Integer number : numbers) {
            if (number % 2 == 0) {
                traditional.add(number * number);
            }
        }
        Collections.sort(traditional);

        List<Integer> streamResult = numbers.stream()
                .filter(number -> number % 2 == 0)
                .map(number -> number * number)
                .sorted()
                .collect(Collectors.toList());

        System.out.println("Traditional: " + traditional);
        System.out.println("Stream: " + streamResult);
    }
}
Output
Traditional: [100, 400, 900]
Stream: [100, 400, 900]
Two valid styles
Traditional approachStream approach
Uses explicit loop and conditional statementsUses filter(), map(), sorted(), and collect()
Mutation of the result list is visiblePipeline expresses transformations declaratively
Often straightforward for stateful control flowOften concise for data transformation chains
Easy to debug one statement at a timeEasy to read when stages have clear purposes
Check your understanding: Do streams always replace loops?

No. Choose the form that communicates the processing clearly.