Chapter 6: Functional Programming, Lambda Expressions & Stream API


Introduction

Java 8 introduced a major evolution in the Java programming language.

Before Java 8, developers primarily used:

  • Classes
  • Objects
  • Loops
  • Iterators

While these approaches remain important, modern enterprise applications increasingly leverage:

  • Functional Programming
  • Lambda Expressions
  • Functional Interfaces
  • Stream API

These features help developers write:

  • Cleaner Code
  • More Readable Code
  • Less Boilerplate
  • More Maintainable Applications

In this chapter, we will learn modern Java programming techniques and apply them to our Institute Placement Management System (IPMS).


What You Will Learn

By the end of this chapter, you will:

  • Understand Functional Programming
  • Learn Lambda Expressions
  • Create Functional Interfaces
  • Use Built-in Functional Interfaces
  • Work with Method References
  • Use Stream API
  • Perform Filtering
  • Perform Mapping
  • Perform Sorting
  • Perform Aggregations
  • Process Collections Efficiently
  • Build Modern IPMS Features

Understanding Functional Programming

Functional Programming focuses on:

  • Functions
  • Immutability
  • Declarative Programming

Instead of:

for(int i=0;i<list.size();i++) {

}

we focus on:

students.stream()

Traditional Programming

List<String> names =
        new ArrayList<>();

names.add("John Doe");

names.add("Sara Elise");

for(String name : names) {

    System.out.println(name);
}

Functional Style

names.forEach(
        name ->
                System.out.println(name));

Benefits

  • Less Code
  • Better Readability
  • Easier Maintenance
  • Better Collection Processing

Functional Programming Overview

Functional Programming Overview


Lambda Expressions

A Lambda Expression is an anonymous function.


Traditional Approach

Runnable runnable =
        new Runnable() {

            @Override
            public void run() {

                System.out.println(
                        "Running");
            }
        };

Lambda Approach

Runnable runnable =
        () ->
                System.out.println(
                        "Running");

Lambda Syntax

(parameters) -> expression

Example

(name) ->
        System.out.println(name);

No Parameter Lambda

() ->
        System.out.println(
                "Welcome");

Single Parameter Lambda

name ->
        System.out.println(name);

Multiple Parameters

(a, b) ->
        a + b

Lambda Returning Value

(a, b) ->
        a + b

Example

Calculator calculator =
        (a, b) -> a + b;

Functional Interfaces

A Functional Interface contains only one abstract method.


Example

@FunctionalInterface
public interface Calculator {

    int add(
            int a,
            int b);
}

Lambda Implementation

Calculator calculator =
        (a, b) -> a + b;

Calling

System.out.println(
        calculator.add(10,20));

Output

30

Functional Interface Example

@FunctionalInterface
public interface Notification {

    void send(
            String message);
}

Implementation

Notification notification =
        message ->
                System.out.println(
                        message);

Using

notification.send(
        "Placement Drive Created");

Built-in Functional Interfaces

Java provides several commonly used interfaces.

Package:

java.util.function

Common Interfaces

Interface Method
Predicate test()
Function apply()
Consumer accept()
Supplier get()

Predicate

Used for filtering.


Predicate<Integer> eligible =
        cgpa -> cgpa >= 7;

Example

System.out.println(
        eligible.test(8));

Output

true

Function

Transforms data.


Function<String,Integer> length =
        name -> name.length();

Example

System.out.println(
        length.apply(
                "John Doe"));

Consumer

Consumes data.


Consumer<String> display =
        name ->
                System.out.println(
                        name);

Example

display.accept(
        "Sara Elise");

Supplier

Produces data.


Supplier<String> company =
        () -> "Google";

Example

System.out.println(
        company.get());

Method References

Method references provide cleaner syntax.


Lambda

names.forEach(
        name ->
                System.out.println(name));

Method Reference

names.forEach(
        System.out::println);

Static Method Reference

ClassName::methodName

Instance Method Reference

object::methodName

Constructor Reference

Student::new

Introduction to Streams

Streams process collections efficiently.


Traditional Collection Processing

for(Student student
        : students) {

    if(student.getCgpa() >= 8) {

        System.out.println(
                student.getName());
    }
}

Stream Approach

students.stream()
        .filter(
                student ->
                        student.getCgpa() >= 8)
        .forEach(
                student ->
                        System.out.println(
                                student.getName()));

Stream Architecture

Stream API Architecture


Creating Streams

From Collection

students.stream();

From Array

Arrays.stream(numbers);

From Values

Stream.of(
        1,2,3,4);

Filtering Data

students.stream()
        .filter(
                student ->
                        student.getCgpa() >= 8)
        .forEach(
                System.out::println);

Mapping Data

students.stream()
        .map(
                Student::getName)
        .forEach(
                System.out::println);

Example Output

John Doe

Sara Elise

Sorting Data

students.stream()
        .sorted(
                Comparator.comparing(
                        Student::getName))
        .forEach(
                System.out::println);

Reverse Sorting

students.stream()
        .sorted(
                Comparator.comparing(
                        Student::getCgpa)
                        .reversed())
        .forEach(
                System.out::println);

Limiting Records

students.stream()
        .limit(5)
        .forEach(
                System.out::println);

Skipping Records

students.stream()
        .skip(5)
        .forEach(
                System.out::println);

Collecting Results

List<Student> toppers =
        students.stream()
                .filter(
                        student ->
                                student.getCgpa() >= 8)
                .collect(
                        Collectors.toList());

Counting Records

long count =
        students.stream()
                .count();

Finding First Record

Optional<Student> student =
        students.stream()
                .findFirst();

Finding Any Record

Optional<Student> student =
        students.stream()
                .findAny();

Matching Records

boolean result =
        students.stream()
                .allMatch(
                        student ->
                                student.getCgpa() >= 7);

anyMatch

boolean result =
        students.stream()
                .anyMatch(
                        student ->
                                student.getCgpa() >= 9);

noneMatch

boolean result =
        students.stream()
                .noneMatch(
                        student ->
                                student.getCgpa() < 0);

Aggregation

Sum

double total =
        students.stream()
                .mapToDouble(
                        Student::getCgpa)
                .sum();

Average

double average =
        students.stream()
                .mapToDouble(
                        Student::getCgpa)
                .average()
                .orElse(0);

Maximum

double highest =
        students.stream()
                .mapToDouble(
                        Student::getCgpa)
                .max()
                .orElse(0);

Minimum

double lowest =
        students.stream()
                .mapToDouble(
                        Student::getCgpa)
                .min()
                .orElse(0);

Grouping Data

Map<String,List<Student>>
studentsByBranch =
students.stream()
        .collect(
                Collectors.groupingBy(
                        Student::getBranch));

Example

Map<String,List<Student>>
studentsByBranch =
students.stream()
        .collect(
                Collectors.groupingBy(
                        Student::getBranch));

Partitioning Data

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

Stream Pipeline

students.stream()
        .filter(
                student ->
                        student.getCgpa() >= 8)
        .sorted(
                Comparator.comparing(
                        Student::getName))
        .map(
                Student::getName)
        .forEach(
                System.out::println);

Parallel Streams

Used for large datasets.


students.parallelStream()
        .forEach(
                System.out::println);

Sequential Stream

students.stream()
        .forEach(
                System.out::println);

IPMS Student Example

List<Student> students =
        new ArrayList<>();

students.add(
        new Student(
                101L,
                "John Doe",
                8.75));

students.add(
        new Student(
                102L,
                "Sara Elise",
                9.20));

Eligible Students

students.stream()
        .filter(
                student ->
                        student.getCgpa() >= 8)
        .forEach(
                System.out::println);

Placement Report

students.stream()
        .sorted(
                Comparator.comparing(
                        Student::getCgpa)
                        .reversed())
        .forEach(
                System.out::println);

IPMS Stream Processing

IPMS Stream Processing Architecture


Mini Project

Build Student Analytics Module.

Requirements:

  • Store Students
  • Filter Eligible Students
  • Sort by CGPA
  • Generate Placement Reports
  • Group by Branch
  • Calculate Average CGPA
  • Find Top Students

Best Practices

  • Prefer Streams for Collection Processing
  • Use Method References
  • Keep Lambdas Small
  • Avoid Side Effects
  • Use Parallel Streams Carefully
  • Use Functional Interfaces

Common Beginner Mistakes

Mistake Correct Approach
Complex Lambdas Keep Lambdas Simple
Overusing Parallel Streams Use Only When Necessary
Ignoring Optional Handle Properly
Long Stream Pipelines Break Into Steps
Using Streams Everywhere Use Appropriate Approach

Practice Exercises

  1. Create Calculator Functional Interface.
  2. Create Notification Functional Interface.
  3. Use Predicate for Student Filtering.
  4. Use Function for Name Transformation.
  5. Use Consumer for Display.
  6. Use Supplier for Company Data.
  7. Filter Students by CGPA.
  8. Sort Students by Name.
  9. Calculate Average CGPA.
  10. Generate Placement Analytics.

Chapter Summary

In this chapter, you learned:

  • Functional Programming
  • Lambda Expressions
  • Functional Interfaces
  • Predicate
  • Function
  • Consumer
  • Supplier
  • Method References
  • Stream API
  • Filtering
  • Mapping
  • Sorting
  • Aggregations
  • Parallel Streams

In the next chapter, we will learn:

  • Multithreading
  • Concurrent Programming
  • Executor Framework
  • Synchronization

These concepts help build scalable and high-performance enterprise applications.

Need help with your project?
I can help you explore our services or start an inquiry.