Backend Development 16 min read

Java 8 Functional Interfaces and Stream API: Practical Examples and Usage

This article introduces Java 8's lambda expressions, functional interfaces, and the Stream API, explaining key concepts such as lazy and eager evaluation, and demonstrates common stream operations like collect, filter, map, flatMap, max/min, count, and reduce with concrete code examples.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Java 8 Functional Interfaces and Stream API: Practical Examples and Usage

Introduction

Java 8's most significant feature is the introduction of lambda expressions, enabling functional programming where immutable values are processed by functions to produce new values.

1. Important Functional Interfaces in Java

1.1 What is a Functional Interface?

A functional interface is an interface that contains exactly one abstract method and can be used as the type of a lambda expression. It is typically annotated with @FunctionalInterface , which causes the compiler to verify that the interface has only one abstract method. Default and static methods are allowed.

1.2 Common Java 8 Functional Interfaces

Examples of using various functional interfaces are shown below.

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

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

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

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

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

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

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

    /**
     * Demonstrates custom functional interface usage
     * @param worker
     */
    public static void test(Worker worker) {
        String work = worker.work();
        System.out.println(work);
    }

    public interface Worker {
        String work();
    }
}
//9龙的身高高于185吗?:false
//命运由我不由天
//9龙
//10
//false
//6
//我是一个演示的函数式接口

The above code demonstrates lambda usage and a custom functional interface.

Note: Student::getName is a method reference of the form ClassName::methodName , which is a concise way to write lambda expressions.

All examples are based on three classes: OutstandingClass (class), Student (student), and SpecialityEnum (specialty).

2. Common Stream Operations

2.1 collect(Collectors.toList())

Converts a stream to a list (also toSet(), toMap(), etc.). This is an eager operation.

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

2.2 filter

Filters elements using a Predicate . This is a lazy operation.

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

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

2.3 map

Transforms each element using a Function . This is a lazy operation.

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

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

2.4 flatMap

Flattens multiple streams into a single stream. This is a lazy operation.

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

        List
studentList = Stream.of(students,
                asList(
                        new Student("艾斯", 25, 183),
                        new Student("雷利", 48, 176)))
                .flatMap(students1 -> students1.stream())
                .collect(Collectors.toList());
        System.out.println(studentList);
    }
}
//[Student{name='路飞', age=22, stature=175, specialities=null}, 
// Student{name='红发', age=40, stature=180, specialities=null}, 
// Student{name='白胡子', age=50, stature=185, specialities=null}, 
// Student{name='艾斯', age=25, stature=183, specialities=null}, 
// Student{name='雷利', age=48, stature=176, specialities=null}]

2.5 max and min

Finds the maximum or minimum element using a Comparator . This is an eager operation that returns an Optional .

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

        Optional
max = students.stream()
                .max(Comparator.comparing(stu -> stu.getAge()));
        Optional
min = students.stream()
                .min(Comparator.comparing(stu -> stu.getAge()));
        if (max.isPresent()) {
            System.out.println(max.get());
        }
        if (min.isPresent()) {
            System.out.println(min.get());
        }
    }
}
//Student{name='白胡子', age=50, stature=185, specialities=null}
//Student{name='路飞', age=22, stature=175, specialities=null}

Use max.isPresent() to check existence, max.orElse(new Student()) to provide a default, or max.orElseGet(() -> new Student()) with a supplier.

2.6 count

Counts elements, usually after a filter . This is an eager operation.

public class TestCase {
    public static void main(String[] args) {
        List
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(s1 -> s1.getAge() < 45)
                .count();
        System.out.println("年龄小于45岁的人数是:" + count);
    }
}
//年龄小于45岁的人数是:2

2.7 reduce

The reduce operation aggregates a stream into a single value; many other terminal operations are built on it.

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);
    }
}
//10

3. Advanced Collectors

3.1 Collecting to a Value

Collectors are generic utilities that transform a stream into a complex result. Examples use static imports from java.util.stream.Collectors .

public class CollectorsTest {
    public static void main(String[] args) {
        List
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
students2 = new ArrayList<>(students1);
        students2.remove(1);
        OutstandingClass ostClass2 = new OutstandingClass("二班", students2);
        Stream
classStream = Stream.of(ostClass1, ostClass2);
        OutstandingClass outstandingClass = biggestGroup(classStream);
        System.out.println("人数最多的班级是:" + outstandingClass.getName());

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

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

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

3.2 Collecting to Partitions

Using Collectors.partitioningBy to split a collection into two groups based on a predicate.

public class PartitioningByTest {
    public static void main(String[] args) {
        // Assume List
students is initialized
        Map
> listMap = students.stream().collect(
                Collectors.partitioningBy(student -> student.getSpecialities().contains(SpecialityEnum.SING)));
    }
}

3.3 Grouping

Using Collectors.groupingBy to group elements by an arbitrary key, similar to SQL's GROUP BY.

public class GroupingByTest {
    public static void main(String[] args) {
        // Assume List
students is initialized
        Map
> listMap =
                students.stream().collect(Collectors.groupingBy(student -> student.getSpecialities().get(0)));
    }
}

3.4 Joining Strings

Concatenates strings from a stream using Collectors.joining() , optionally specifying a delimiter, prefix, and suffix.

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

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

Conclusion

This article demonstrates how Java 8's functional programming features and Stream API provide clear, concise ways to process collections, and encourages readers to refactor existing code to take advantage of these capabilities.

Combining multiple stream operations yields powerful, chainable workflows that can be adapted to various business scenarios.

backendJavalambdastreamsfunctional interfacescollectors
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login 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.