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<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(() -> "我是一个演示的函数式接口");
}
/**
* 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<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}]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<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}]2.3 map
Transforms each element using a Function. This is a lazy operation.
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:
//[路飞, 红发, 白胡子]2.4 flatMap
Flattens multiple Streams into a single Stream. Lazy evaluation.
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(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<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(Student::getAge));
Optional<Student> 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<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岁的人数是: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<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(c -> c.getStudents().size())))
.orElseGet(OutstandingClass::new);
}
private static double averageNumberOfStudent(List<Student> 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<Student> students is initialized
Map<Boolean, List<Student>> 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<Student> students is initialized
Map<SpecialityEnum, List<Student>> 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<Student> 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
