Skills101

Java Programming · Lesson 13 of 15 · Updated ·

Java 8 Features — Lambdas, Method References, Streams and Optional

Java 8 changed how Java code looks. Lambdas and streams let you describe what you want from a collection instead of writing loops — and they come up in almost every Java interview.

1. Lambda expressions

A lambda is a short anonymous function: (parameters) -> expression. It implements a functional interface (one abstract method).

import java.util.*;
import java.util.function.*;

public class Main {
    public static void main(String[] args) {
        Function<Integer, Integer> square = x -> x * x;
        Predicate<String> isLong = s -> s.length() > 4;
        BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
        Supplier<String> hello = () -> "hello";
        Consumer<String> shout = s -> System.out.println(s.toUpperCase());

        System.out.println(square.apply(6) + " " + isLong.test("Java") + " " + add.apply(2, 3) + " " + hello.get());
        shout.accept("lambdas");
    }
}
36 false 5 hello
LAMBDAS
InterfaceTakes → givesMethod
Function<T, R>T → Rapply
Predicate<T>T → booleantest
Consumer<T>T → nothingaccept
Supplier<T>nothing → Tget

2. Method references

When a lambda just calls one method, write Class::method:

  • s -> s.toUpperCase() → String::toUpperCase
  • x -> System.out.println(x) → System.out::println

3. Streams

A stream pipeline: a source list flows through filter (keep some) and map (transform) into collect (make the result). Nothing runs until the last step

A stream is a pipeline: a source → zero or more intermediate operations (filter, map, sorted, distinct, limit) → one terminal operation (collect, forEach, count, reduce, sum). Streams don't change the original collection, and nothing runs until the terminal operation (lazy).

import java.util.*;
import java.util.stream.*;

public class Main {
    public static void main(String[] args) {
        List<String> names = List.of("asha", "ravi", "neha", "kabir", "om");
        List<String> result = names.stream()
            .filter(n -> n.length() > 3)
            .map(String::toUpperCase)
            .sorted()
            .collect(Collectors.toList());
        System.out.println(result);

        List<Integer> nums = List.of(4, 8, 15, 16, 23, 42);
        int sumOfEvens = nums.stream().filter(n -> n % 2 == 0).mapToInt(Integer::intValue).sum();
        long countBig = nums.stream().filter(n -> n > 10).count();
        int product = nums.stream().limit(3).reduce(1, (a, b) -> a * b);
        System.out.println(sumOfEvens + " " + countBig + " " + product);
    }
}
[ASHA, KABIR, NEHA, RAVI]
70 4 480

Grouping and statistics

import java.util.*;
import java.util.stream.*;

record Student(String name, String city, int marks) {}

public class Main {
    public static void main(String[] args) {
        List<Student> list = List.of(
            new Student("Asha", "Jaipur", 92), new Student("Ravi", "Pune", 74),
            new Student("Neha", "Jaipur", 85), new Student("Om", "Pune", 66));

        Map<String, List<String>> byCity = list.stream()
            .collect(Collectors.groupingBy(Student::city, TreeMap::new,
                     Collectors.mapping(Student::name, Collectors.toList())));
        System.out.println(byCity);

        double avg = list.stream().mapToInt(Student::marks).average().orElse(0);
        String topper = list.stream().max(Comparator.comparingInt(Student::marks)).map(Student::name).orElse("none");
        System.out.println(avg + " " + topper);
    }
}
{Jaipur=[Asha, Neha], Pune=[Ravi, Om]}
79.25 Asha

(record — Java 16+ — is a short way to write a class that just holds data.)

4. Optional: no more surprise nulls

Optional<T> is a box that may or may not contain a value. It forces you to think about the empty case.

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Optional<String> found = List.of("java", "python").stream().filter(s -> s.startsWith("p")).findFirst();
        Optional<String> missing = List.of("java").stream().filter(s -> s.startsWith("z")).findFirst();
        System.out.println(found.orElse("none") + " " + missing.orElse("none") + " " + missing.isPresent());
        System.out.println(found.map(String::length).orElse(0));
    }
}
python none false
6

Common mistakes

  • Reusing a stream after a terminal operation (IllegalStateException) — create a new one.
  • Writing long, unreadable stream chains; a loop can be clearer.
  • Calling optional.get() without checking — use orElse, orElseThrow, map.

Quick recap

  • Lambda: (x) -> …, implements a functional interface (Function, Predicate, Consumer, Supplier).
  • Method references: Class::method.
  • Stream = source → intermediate ops (lazy) → one terminal op.
  • groupingBy, mapToInt().sum()/average(), reduce, Optional.

Practice what you learned

📝 Quiz: Java 8 Lambdas and Streams5 questions · instant answers & explanations · +10 XP · counts towards your free certificateTake the quiz →