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.
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岁的人数是:22.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);
}
}
//103. 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.6666666666666643.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.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.