【Day25】Lambda 表达式:函数式编程入门与集合遍历优化
Lambda 表达式是 Java8 的核心特性之一,也是函数式编程的基础 —— 它让代码从「面向对象的冗长」变得「函数式的简洁」,尤其在集合遍历、线程创建、回调函数等场景中,能大幅简化代码。今天我们从「语法 - 用法 - 实战」全维度拆解 Lambda,掌握其核心用法与优化技巧。
一、核心认知:Lambda 表达式是什么?
1. 定义
Lambda 表达式本质是「匿名函数」—— 没有名称的函数,它允许将行为(逻辑)作为参数传递给方法,而无需像传统方式那样定义一个类 / 接口来封装这个行为。
2. 核心价值
- 简化代码:替代匿名内部类,减少模板代码(比如 Runnable、Comparator);
- 函数式编程:关注「做什么」而非「怎么做」,符合函数式编程思想;
- 集合优化:配合 Stream 流(Day26)实现高效的集合数据处理;
- 并行处理:轻松实现集合的并行遍历,提升处理效率。
3. 对比:传统写法 vs Lambda 写法
以创建线程为例,直观感受 Lambda 的简洁:
java
运行
public class LambdaCompareDemo {
public static void main(String[] args) {
// 传统方式:匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("传统方式:匿名内部类创建线程");
}
}).start();
// Lambda方式:一行代码搞定
new Thread(() -> System.out.println("Lambda方式:简洁创建线程")).start();
}
}
二、Lambda 基础:语法与函数式接口
1. Lambda 核心语法
Lambda 表达式的语法格式可总结为:
plaintext
(参数列表) -> { 执行逻辑 }
语法简化规则(核心)
| 场景 | 简化示例 | 说明 |
|---|---|---|
| 无参数 | () -> System.out.println("hello") |
括号不能省略 |
| 单个参数 | x -> x * 2 |
括号可省略,参数类型可省略(自动推断) |
| 多个参数 | (x, y) -> x + y |
括号不能省略,类型可省略 |
| 单行执行逻辑 | (x, y) -> x + y |
大括号和 return 可省略 |
| 多行执行逻辑 | (x, y) -> { int sum = x + y; return sum; } |
必须加大括号和 return |
2. 核心前提:函数式接口
Lambda 表达式只能用于函数式接口—— 即「只有一个抽象方法的接口」(Object 类的方法除外)。
Java8 提供了@FunctionalInterface注解来标记函数式接口(可选,但推荐添加,编译器会校验是否符合规范):
java
运行
/**
* 自定义函数式接口
*/
@FunctionalInterface
interface Calculator {
// 唯一抽象方法
int calculate(int a, int b);
// 允许有默认方法(Java8接口新特性)
default void printLog() {
System.out.println("计算日志:执行加法运算");
}
}
// Lambda实现函数式接口
public class FunctionalInterfaceDemo {
public static void main(String[] args) {
// Lambda实现calculate方法(加法)
Calculator add = (a, b) -> a + b;
System.out.println("10+20=" + add.calculate(10, 20)); // 30
// Lambda实现calculate方法(乘法)
Calculator multiply = (a, b) -> a * b;
System.out.println("10*20=" + multiply.calculate(10, 20)); // 200
// 调用默认方法
add.printLog(); // 计算日志:执行加法运算
}
}
Java8 内置核心函数式接口
无需自定义,直接使用 JDK 提供的函数式接口,覆盖绝大多数场景:
| 接口名 | 抽象方法 | 用途 | 示例 |
|---|---|---|---|
| Runnable | void run() | 无参数无返回值 | 线程创建 |
| Consumer<T> | void accept(T t) | 消费一个参数,无返回值 | 集合遍历 |
| Supplier<T> | T get() | 无参数,返回一个值 | 数据生成 |
| Function<T, R> | R apply(T t) | 接收 T,返回 R | 数据转换(如 String→Integer) |
| Predicate<T> | boolean test(T t) | 接收 T,返回布尔值 | 条件判断(如过滤集合) |
三、核心实战:Lambda 优化集合遍历
场景:遍历 List 集合(传统写法 vs Lambda 写法)
java
运行
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class LambdaListTraverseDemo {
public static void main(String[] args) {
List<String> userList = new ArrayList<>();
userList.add("张三");
userList.add("李四");
userList.add("王五");
// ========== 传统写法1:普通for循环 ==========
System.out.println("普通for循环:");
for (int i = 0; i < userList.size(); i++) {
System.out.println(userList.get(i));
}
// ========== 传统写法2:增强for循环 ==========
System.out.println("\n增强for循环:");
for (String userName : userList) {
System.out.println(userName);
}
// ========== 传统写法3:forEach+匿名内部类 ==========
System.out.println("\nforEach+匿名内部类:");
userList.forEach(new Consumer<String>() {
@Override
public void accept(String userName) {
System.out.println(userName);
}
});
// ========== Lambda写法1:简化版 ==========
System.out.println("\nLambda简化版:");
userList.forEach(userName -> System.out.println(userName));
// ========== Lambda写法2:方法引用(进一步简化) ==========
System.out.println("\nLambda+方法引用:");
userList.forEach(System.out::println);
}
}
场景:遍历 Map 集合(Lambda 优化)
java
运行
import java.util.HashMap;
import java.util.Map;
public class LambdaMapTraverseDemo {
public static void main(String[] args) {
Map<Integer, String> userMap = new HashMap<>();
userMap.put(1, "张三");
userMap.put(2, "李四");
userMap.put(3, "王五");
// ========== 传统写法:entrySet遍历 ==========
System.out.println("传统Map遍历:");
for (Map.Entry<Integer, String> entry : userMap.entrySet()) {
System.out.println("ID:" + entry.getKey() + ",姓名:" + entry.getValue());
}
// ========== Lambda写法 ==========
System.out.println("\nLambda Map遍历:");
userMap.forEach((id, name) -> System.out.println("ID:" + id + ",姓名:" + name));
}
}
场景:集合排序(Lambda 替代 Comparator)
java
运行
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 实体类:用户
*/
class User {
private Integer id;
private String name;
private Integer age;
public User(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
// getter/setter/toString省略(实际开发中需添加)
@Override
public String toString() {
return "User{id=" + id + ", name='" + name + "', age=" + age + "}";
}
public Integer getAge() {
return age;
}
}
public class LambdaSortDemo {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();
userList.add(new User(1, "张三", 20));
userList.add(new User(2, "李四", 18));
userList.add(new User(3, "王五", 22));
// ========== 传统写法:Comparator匿名内部类 ==========
System.out.println("传统排序(按年龄升序):");
Collections.sort(userList, new java.util.Comparator<User>() {
@Override
public int compare(User u1, User u2) {
return u1.getAge() - u2.getAge();
}
});
userList.forEach(System.out::println);
// ========== Lambda写法:简化Comparator ==========
System.out.println("\nLambda排序(按年龄降序):");
Collections.sort(userList, (u1, u2) -> u2.getAge() - u1.getAge());
userList.forEach(System.out::println);
}
}
运行结果
plaintext
传统排序(按年龄升序):
User{id=2, name='李四', age=18}
User{id=1, name='张三', age=20}
User{id=3, name='王五', age=22}
Lambda排序(按年龄降序):
User{id=3, name='王五', age=22}
User{id=1, name='张三', age=20}
User{id=2, name='李四', age=18}
四、进阶实战:Lambda 结合函数式接口实现通用功能
场景:通用数据处理器(Function 接口)
实现一个通用的数据转换工具,支持任意类型的转换(如 String→Integer、User→String):
java
运行
import java.util.function.Function;
/**
* 通用数据处理器:基于Function接口
* @param <T> 输入类型
* @param <R> 输出类型
*/
class DataProcessor<T, R> {
private Function<T, R> converter;
// 构造器注入Lambda表达式
public DataProcessor(Function<T, R> converter) {
this.converter = converter;
}
// 执行转换
public R process(T input) {
return converter.apply(input);
}
}
public class LambdaFunctionDemo {
public static void main(String[] args) {
// 1. String→Integer转换
DataProcessor<String, Integer> strToInt = new DataProcessor<>(s -> Integer.parseInt(s));
System.out.println("字符串转数字:" + strToInt.process("123")); // 123
// 2. User→String(提取姓名)
User user = new User(1, "张三", 20);
DataProcessor<User, String> userToName = new DataProcessor<>(u -> "用户名:" + u.toString().split(",")[1].split("=")[1]);
System.out.println("用户对象转姓名:" + userToName.process(user)); // 用户名:'张三'
// 3. Integer→String(格式化)
DataProcessor<Integer, String> intToStr = new DataProcessor<>(num -> "编号:" + String.format("%03d", num));
System.out.println("数字格式化:" + intToStr.process(5)); // 编号:005
}
}
场景:条件过滤(Predicate 接口)
实现通用的集合过滤工具,支持任意条件过滤:
java
运行
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class LambdaPredicateDemo {
/**
* 通用集合过滤方法
* @param list 源集合
* @param condition 过滤条件(Predicate)
* @param <T> 集合元素类型
* @return 过滤后的集合
*/
public static <T> List<T> filter(List<T> list, Predicate<T> condition) {
List<T> result = new ArrayList<>();
for (T t : list) {
if (condition.test(t)) {
result.add(t);
}
}
return result;
}
public static void main(String[] args) {
List<User> userList = new ArrayList<>();
userList.add(new User(1, "张三", 20));
userList.add(new User(2, "李四", 18));
userList.add(new User(3, "王五", 22));
userList.add(new User(4, "赵六", 25));
// 1. 过滤年龄>20的用户
List<User> ageGT20 = filter(userList, u -> u.getAge() > 20);
System.out.println("年龄>20的用户:");
ageGT20.forEach(System.out::println);
// 2. 过滤姓名包含"张"的用户
List<User> nameContainZhang = filter(userList, u -> u.toString().contains("张三"));
System.out.println("\n姓名包含张的用户:");
nameContainZhang.forEach(System.out::println);
}
}
五、避坑指南 & 面试高频考点
1. 避坑要点
- ❌ Lambda 中访问局部变量:局部变量必须是 final 或 effectively final(即实际未修改),否则编译报错;
java
运行
// 错误示例:局部变量count被修改 int count = 0; userList.forEach(u -> { count++; // 编译报错:Variable used in lambda expression should be final or effectively final System.out.println(u); }); - ❌ 过度简化代码:Lambda 追求简洁,但不要为了简化而牺牲可读性(比如多行逻辑仍用一行 Lambda);
- ❌ 忽略异常处理:Lambda 中抛出的检查异常需在内部捕获,或通过函数式接口声明(推荐内部捕获);
- ❌ 误解 Lambda 的本质:Lambda 不是匿名内部类的语法糖 —— 底层实现不同(匿名内部类生成独立 class 文件,Lambda 通过 invokedynamic 指令)。
2. 面试常问
- Q:Lambda 表达式的核心语法是什么?简化规则有哪些?A:核心语法
(参数) -> {逻辑};简化规则包括:单参数省略括号、单行逻辑省略大括号 /return、参数类型自动推断; - Q:函数式接口的定义是什么?Java8 内置了哪些核心函数式接口?A:只有一个抽象方法的接口;核心有 Consumer(消费)、Supplier(供给)、Function(转换)、Predicate(判断);
- Q:Lambda 表达式中为什么局部变量必须是 final?A:Lambda 执行可能在另一个线程,为了保证变量的线程安全和一致性,要求局部变量不可变;
- Q:Lambda 和匿名内部类的区别?A:① 底层实现不同(Lambda 是 invokedynamic,匿名内部类是生成 class);② Lambda 只能用于函数式接口,匿名内部类可用于任意接口;③ Lambda 访问局部变量需 final,匿名内部类则是 “隐式 final”。
六、实战小练习
实现一个通用的计算器工具类:
- 基于函数式接口定义加减乘除四种运算;
- 用 Lambda 表达式实现每种运算的逻辑;
- 编写测试方法,支持动态选择运算类型并执行计算。
(提示:自定义函数式接口Calculate<T>,包含方法T calc(T a, T b),用 Lambda 分别实现加减乘除)
总结
- Lambda 核心:本质是匿名函数,仅能用于函数式接口(唯一抽象方法),核心语法
(参数) -> {逻辑}; - 核心优势:简化匿名内部类代码,优化集合遍历 / 排序,支持函数式编程思想;
- 关键用法:结合 Java8 内置函数式接口(Consumer/Function/Predicate)实现通用功能,大幅减少重复代码。
通过今天的学习,你已掌握 Lambda 表达式的核心用法,它不仅能让代码更简洁,也是后续学习 Stream 流(Day26)的基础。下一节我们将结合 Lambda 和 Stream,实现更高效的集合数据处理。
更多推荐



所有评论(0)