Fundamentals 15 min read

Unlock Java 8: Master Lambda Expressions and Stream API with Real Code Examples

This article introduces Java 8’s key functional programming features—lambda expressions, functional interfaces, and the Stream API—explaining concepts such as lazy and eager evaluation, and demonstrating common operations like filter, map, flatMap, collect, reduce, and grouping with clear code examples.

Programmer DD
Programmer DD
Programmer DD
Unlock Java 8: Master Lambda Expressions and Stream API with Real Code Examples

1. Introduction

Java 8’s biggest feature is the introduction of lambda expressions, enabling functional programming by passing behavior as immutable values processed by functions.

2. Important Functional Interfaces in Java

2.1 What is a functional interface

A functional interface has a single abstract method and can be used as the type of a lambda expression. The @FunctionalInterface annotation forces the compiler to verify this constraint. It may also contain default and static methods.

2.1.1 Common built‑in functional interfaces

public class Test {
    public static void main(String[] args) {
        Predicate<Integer> predicate = x -> x > 185;
        Student student = new Student("9龙", 23, 175);
        System.out.println("9龙的身高高于185吗?:" + predicate.test(student.getStature()));

        Consumer<String> consumer = System.out::println;
        consumer.accept("命运由我不由天");

        Function<Student, String> function = Student::getName;
        String name = function.apply(student);
        System.out.println(name);

        Supplier<Integer> supplier = () -> Integer.valueOf(BigDecimal.TEN.toString());
        System.out.println(supplier.get());

        UnaryOperator<Boolean> unaryOperator = uglily -> !uglily;
        Boolean apply2 = unaryOperator.apply(true);
        System.out.println(apply2);

        BinaryOperator<Integer> operator = (x, y) -> x * y;
        Integer integer = operator.apply(2, 3);
        System.out.println(integer);

        test(() -> "我是一个演示的函数式接口");
    }

    /** 演示自定义函数式接口使用 */
    public static void test(Worker worker) {
        String work = worker.work();
        System.out.println(work);
    }

    public interface Worker {
        String work();
    }
}

The example demonstrates using lambda‑based interfaces such as Predicate, Consumer, Function, Supplier, UnaryOperator, BinaryOperator, and a custom functional interface.

2.2 Lazy and eager evaluation

Lazy evaluation describes operations that return another Stream, allowing chaining similar to the Builder pattern; the final result is obtained through an eager operation that produces a concrete value.

3. Common Stream Operations

3.1 collect(Collectors.toList())

Converts a Stream into a List (also toSet, toMap, etc.) – an eager operation.

public class TestCase {
    public static void main(String[] args) {
        List<Student> studentList = Stream.of(
            new Student("路飞", 22, 175),
            new Student("红发", 40, 180),
            new Student("白胡子", 50, 185)
        ).collect(Collectors.toList());
        System.out.println(studentList);
    }
}
// Output:
//[Student{name='路飞', age=22, stature=175, specialities=null},
// Student{name='红发', age=40, stature=180, specialities=null},
// Student{name='白胡子', age=50, stature=185, specialities=null}]

3.2 filter

Filters elements using a Predicate; the operation is lazy.

public class TestCase {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>(3);
        students.add(new Student("路飞", 22, 175));
        students.add(new Student("红发", 40, 180));
        students.add(new Student("白胡子", 50, 185));

        List<Student> list = students.stream()
            .filter(stu -> stu.getStature() < 180)
            .collect(Collectors.toList());
        System.out.println(list);
    }
}
// Output:
//[Student{name='路飞', age=22, stature=175, specialities=null}]

Example filters students with height less than 180.

3.3 map

Transforms each element, internally using the Function interface; lazy.

public class TestCase {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>(3);
        students.add(new Student("路飞", 22, 175));
        students.add(new Student("红发", 40, 180));
        students.add(new Student("白胡子", 50, 185));

        List<String> names = students.stream()
            .map(Student::getName)
            .collect(Collectors.toList());
        System.out.println(names);
    }
}
// Output:
//[路飞, 红发, 白胡子]

3.4 flatMap

Merges multiple Streams into one; lazy.

public class TestCase {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>(3);
        students.add(new Student("路飞", 22, 175));
        students.add(new Student("红发", 40, 180));
        students.add(new Student("白胡子", 50, 185));

        List<Student> studentList = Stream.of(
                students,
                asList(
                    new Student("艾斯", 25, 183),
                    new Student("雷利", 48, 176)
                ))
            .flatMap(students1 -> students1.stream())
            .collect(Collectors.toList());
        System.out.println(studentList);
    }
}
// Output:
//[Student{name='路飞', ...}, Student{name='红发', ...}, Student{name='白胡子', ...}, Student{name='艾斯', ...}, Student{name='雷利', ...}]

3.5 max and min

Finds the maximum or minimum element using a Comparator, returning an Optional to avoid null pointer exceptions.

public class TestCase {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>(3);
        students.add(new Student("路飞", 22, 175));
        students.add(new Student("红发", 40, 180));
        students.add(new Student("白胡子", 50, 185));

        Optional<Student> max = students.stream()
            .max(Comparator.comparing(stu -> stu.getAge()));
        Optional<Student> min = students.stream()
            .min(Comparator.comparing(stu -> stu.getAge()));

        max.ifPresent(s -> System.out.println(s));
        min.ifPresent(s -> System.out.println(s));
    }
}
// Output:
//Student{name='白胡子', age=50, ...}
//Student{name='路飞', age=22, ...}

3.6 count

Counts elements, usually combined with filter; eager.

public class TestCase {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>(3);
        students.add(new Student("路飞", 22, 175));
        students.add(new Student("红发", 40, 180));
        students.add(new Student("白胡子", 50, 185));

        long count = students.stream()
            .filter(s -> s.getAge() < 45)
            .count();
        System.out.println("年龄小于45岁的人数是:" + count);
    }
}
// Output:
//年龄小于45岁的人数是:2

3.7 reduce

Reduces a Stream to a single value; the example sums numbers 1‑4.

public class TestCase {
    public static void main(String[] args) {
        Integer reduce = Stream.of(1, 2, 3, 4)
            .reduce(0, (acc, x) -> acc + x);
        System.out.println(reduce);
    }
}
// Output:
//10

4. Advanced Collectors

4.1 Common collectors

Collectors are generic tools that turn a Stream into complex results. Examples include averagingInt, maxBy, minBy, partitioningBy, groupingBy, and joining.

public class CollectorsTest {
    public static void main(String[] args) {
        List<Student> students1 = new ArrayList<>(3);
        students1.add(new Student("路飞", 23, 175));
        students1.add(new Student("红发", 40, 180));
        students1.add(new Student("白胡子", 50, 185));

        OutstandingClass ostClass1 = new OutstandingClass("一班", students1);

        List<Student> students2 = new ArrayList<>(students1);
        students2.remove(1);
        OutstandingClass ostClass2 = new OutstandingClass("二班", students2);

        Stream<OutstandingClass> classStream = Stream.of(ostClass1, ostClass2);
        OutstandingClass biggest = biggestGroup(classStream);
        System.out.println("人数最多的班级是:" + biggest.getName());

        System.out.println("一班平均年龄是:" + averageNumberOfStudent(students1));
    }

    private static OutstandingClass biggestGroup(Stream<OutstandingClass> classes) {
        return classes.collect(maxBy(comparing(oc -> oc.getStudents().size())))
            .orElseGet(OutstandingClass::new);
    }

    private static double averageNumberOfStudent(List<Student> students) {
        return students.stream().collect(averagingInt(Student::getAge));
    }
}
// Output:
//人数最多的班级是:一班
//一班平均年龄是:37.666666666666664

4.2 partitioningBy

Splits data into two groups based on a Predicate.

Map<Boolean, List<Student>> map = students.stream()
    .collect(Collectors.partitioningBy(s -> s.getSpecialities().contains(SpecialityEnum.SING)));

4.3 groupingBy

Groups data by an arbitrary key, similar to SQL GROUP BY.

Map<SpecialityEnum, List<Student>> map = students.stream()
    .collect(Collectors.groupingBy(s -> s.getSpecialities().get(0)));

4.4 joining

Concatenates strings from a Stream, optionally with delimiter, prefix, and suffix.

String names = students.stream()
    .map(Student::getName)
    .collect(Collectors.joining(",", "[", "]"));
System.out.println(names);
// Output: [路飞,红发,白胡子]

5. Conclusion

The article shows how Java 8’s functional features and Stream API enable concise, expressive code for common collection‑processing tasks. Readers are encouraged to refactor existing codebases to take advantage of these capabilities.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Lambdafunctional programmingStream APIJava 8Functional InterfacesCollectors
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.