Java 8 Functional Interfaces and Stream Operations: Concepts, Examples, and Advanced Collectors
This article introduces Java 8's functional interfaces and lambda expressions, explains immutable values and function composition, demonstrates common stream operations such as collect, filter, map, flatMap, max/min, count, and reduce, and shows how to use advanced collectors like groupingBy, partitioningBy, and joining with practical code examples.
Hello everyone, I'm Lei.
Important Functional Interfaces in Java
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. When a class is annotated with @FunctionalInterface, the compiler checks that it has only one abstract method; otherwise a compilation error occurs. The interface may contain multiple default or static methods.
Note
At the end of the article you will find a link to a 7701‑page collection of interview questions from major internet companies.
1.1 Common Functional Interfaces Built into Java 8
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 a 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 demonstrates the use of lambda expressions with built‑in functional interfaces and a custom functional interface.
Next, we explore how Java 8 integrates functional interfaces into streams to process collections efficiently. Note that the method reference syntax Student::getName is a concise way to write a lambda expression ( student -> student.getName() ).
1.2 Lazy Evaluation and Eager Evaluation
Lazy evaluation describes a Stream that only defines operations; the result is also a Stream, and the actual computation is deferred until a terminal operation is invoked (eager evaluation).
2. Common Stream Operations
2.1 collect(Collectors.toList())
Converts a Stream into a List . Similar collectors exist for Set and Map . 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);
}
}
// 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}]2.2 filter
Filters elements based on a Predicate . This is a lazy operation.
Example: select students with height less than 180.
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);
}
}
// Output:
//[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::getName)
.collect(Collectors.toList());
System.out.println(names);
}
}
// Output:
//[路飞, 红发, 白胡子]2.4 flatMap
Flattens multiple Streams into a single Stream. Lazy evaluation.
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(list -> list.stream())
.collect(Collectors.toList());
System.out.println(studentList);
}
}
// Output includes all five students.2.5 max and min
Find the maximum or minimum element using a Comparator . This is an eager operation that returns an Optional to avoid null pointers.
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(Student::getAge));
Optional
min = students.stream()
.min(Comparator.comparing(Student::getAge));
max.ifPresent(System.out::println);
min.ifPresent(System.out::println);
}
}
// Output:
//Student{name='白胡子', age=50, stature=185, specialities=null}
//Student{name='路飞', age=22, stature=175, specialities=null}2.6 count
Counts elements that satisfy a predicate, usually combined with filter . Eager evaluation.
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(s -> s.getAge() < 45)
.count();
System.out.println("年龄小于45岁的人数是:" + count);
}
}
// Output:
//年龄小于45岁的人数是:22.7 reduce
The reduce operation aggregates a stream into a single value. It underlies many other terminal operations such as sum , max , and min . Eager evaluation.
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:
//10Advanced Collectors
3.1 Collecting to Values
Collectors are utilities that transform a Stream into a complex result. The examples below 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 biggest = biggestGroup(classStream);
System.out.println("人数最多的班级是:" + biggest.getName());
System.out.println("一班平均年龄是:" + averageNumberOfStudent(students1));
}
private static OutstandingClass biggestGroup(Stream
classes) {
return classes.collect(maxBy(comparing(c -> c.getStudents().size())))
.orElseGet(OutstandingClass::new);
}
private static double averageNumberOfStudent(List
students) {
return students.stream().collect(averagingInt(Student::getAge));
}
}
// Output:
//人数最多的班级是:一班
//一班平均年龄是:37.6666666666666643.2 Collecting to Partitions
Partitions a stream into two groups (true/false) based on a Predicate using Collectors.partitioningBy .
public class PartitioningByTest {
public static void main(String[] args) {
// Assume List
students is initialized
Map
> map = students.stream()
.collect(Collectors.partitioningBy(s -> s.getSpecialities().contains(SpecialityEnum.SING)));
}
}3.3 Data Grouping
Groups elements by an arbitrary key using Collectors.groupingBy , similar to SQL's GROUP BY .
public class GroupingByTest {
public static void main(String[] args) {
// Assume List
students is initialized
Map
> map = students.stream()
.collect(Collectors.groupingBy(s -> s.getSpecialities().get(0)));
}
}3.4 String Joining
Concatenates strings from a stream using Collectors.joining . The method can accept a delimiter, a prefix, and a 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);
}
}
// Output:
//[路飞,红发,白胡子]The article concludes with links to additional resources and a promotion for a 7701‑page PDF containing interview questions from major internet companies.
Architect's Tech Stack
Java backend, microservices, distributed systems, containerized programming, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.