Java Lambda 表达式与流处理

一、Lambda 表达式

1. Lambda 表达式简介

Lambda 表达式本质上是函数式接口的匿名实现,它用简洁的语法替代了传统的匿名内部类写法,核心格式如下:

复制代码
(参数列表) -> { 方法体 }

举个最经典的对比:

复制代码
// 传统匿名内部类
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello, Java 8!");
    }
};

// Lambda 表达式写法
Runnable lambdaRunnable = () -> System.out.println("Hello, Java 8!");

它的优势在于:

  • 代码行数大幅减少,去掉了冗余的接口声明、方法重写模板
  • 更贴近 "函数式" 的表达,逻辑直接聚焦在 "要做什么",而非 "怎么写模板"

2. Lambda 表达式实现函数式接口

Lambda 表达式只能用于函数式接口 (只有一个抽象方法的接口),比如 RunnableCallableComparator,以及 java.util.function 包下的内置函数式接口。

我们以 Comparator 为例,给一个列表排序:

复制代码
List<String> words = Arrays.asList("banana", "apple", "cherry");

// 传统写法
Collections.sort(words, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});

// Lambda 写法
Collections.sort(words, (a, b) -> a.length() - b.length());

这里的 Comparator 就是一个函数式接口,Lambda 表达式直接实现了它唯一的 compare 方法。

3. Lambda 表达式调用外部变量

Lambda 表达式可以访问外部的局部变量,但这些变量默认是隐式 final 的(即使你不写 final,也不能修改),这是为了保证多线程环境下的线程安全。

复制代码
final int limit = 5; // 即使去掉 final 也不能修改
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

numbers.stream()
       .filter(n -> n > limit) // 访问外部变量 limit
       .forEach(System.out::println);

// 错误示例:在 Lambda 中修改外部变量会编译报错
// int count = 0;
// numbers.forEach(n -> count++); // 编译失败!

如果你需要在 Lambda 中修改值,可以使用 AtomicInteger 等包装类:

复制代码
AtomicInteger count = new AtomicInteger(0);
numbers.forEach(n -> count.incrementAndGet());
System.out.println(count.get()); // 输出 6

4. Lambda 表达式与异常处理

Lambda 表达式本身不能直接抛出受检异常,如果你需要处理异常,有两种方式:

  1. 在 Lambda 内部用 try-catch 包裹

  2. 自定义函数式接口,声明 throws 异常

    // 方式1:内部 try-catch
    List<String> files = Arrays.asList("a.txt", "b.txt");
    files.forEach(file -> {
    try {
    Files.readAllLines(Paths.get(file));
    } catch (IOException e) {
    e.printStackTrace();
    }
    });

    // 方式2:自定义带异常的函数式接口
    @FunctionalInterface
    interface FileProcessor {
    void process(String file) throws IOException;
    }

    // 使用自定义接口
    FileProcessor processor = file -> Files.readAllLines(Paths.get(file));


二、方法引用:Lambda 的 "进阶缩写"

方法引用是 Lambda 表达式的进一步简化,当 Lambda 只是调用一个已存在的方法时,就可以用方法引用替代,让代码更简洁。

1. 引用静态方法

语法:类名::静态方法名

复制代码
// Lambda 写法
Function<String, Integer> stringToInt = s -> Integer.parseInt(s);

// 方法引用写法
Function<String, Integer> stringToIntRef = Integer::parseInt;

2. 引用成员方法

语法:对象::实例方法名类名::实例方法名

复制代码
List<String> list = Arrays.asList("a", "b", "c");

// 方式1:对象::实例方法
Consumer<String> printer = System.out::println;
list.forEach(printer);

// 方式2:类名::实例方法(适用于参数为调用者的情况)
Comparator<String> comparator = String::compareTo;

3. 引用带泛型的方法

方法引用也支持泛型,比如 List::add

复制代码
Stream.of(1, 2, 3)
      .forEach(System.out::println); // 引用带泛型的方法

4. 引用构造方法

语法:类名::new,常用于创建对象:

复制代码
// Lambda 写法
Supplier<List<String>> listSupplier = () -> new ArrayList<>();

// 方法引用写法
Supplier<List<String>> listSupplierRef = ArrayList::new;

// 带参数的构造方法引用
Function<Integer, List<Integer>> listCreator = ArrayList::new;
List<Integer> nums = listCreator.apply(10); // 创建初始容量为10的 ArrayList

5. Function 接口:内置函数式接口

java.util.function 包提供了大量内置的函数式接口,最常用的有:

接口 抽象方法 用途
Function<T,R> R apply(T t) 接收一个参数,返回一个结果
Consumer<T> void accept(T t) 接收一个参数,无返回值
Supplier<T> T get() 不接收参数,返回一个结果
Predicate<T> boolean test(T t) 接收一个参数,返回布尔值

它们的用法都和前面的例子类似,比如 Predicate 常用于过滤:

复制代码
Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(4)); // true

三、Stream 流处理:集合操作的 "流水线"

Stream 是 Java 8 引入的流处理 API,它可以让你以声明式的方式处理集合数据,就像工厂流水线一样,对数据进行过滤、映射、排序、收集等操作,代码更简洁,也更容易实现并行处理。

1. Stream 接口简介

Stream 的操作分为三类:

  1. 创建流:从集合、数组、文件等数据源创建流

  2. 中间操作:对流进行处理(过滤、映射等),返回新的流(可以链式调用)

  3. 终端操作 :触发计算,关闭流(比如 forEachcollect

    // 从集合创建流
    List<String> list = Arrays.asList("a", "b", "c");
    Stream<String> stream = list.stream();

    // 并行流
    Stream<String> parallelStream = list.parallelStream();

2. Optional 类:告别空指针异常

Optional 是一个容器类,用来解决空指针异常问题,它可以包裹一个可能为 null 的值,提供安全的处理方式。

复制代码
// 创建 Optional 对象
Optional<String> opt = Optional.ofNullable("hello");
Optional<String> emptyOpt = Optional.empty();

// 安全获取值
String value = opt.orElse("default"); // 如果 opt 为 null,返回默认值
String value2 = opt.orElseGet(() -> "default"); // 延迟加载默认值
opt.ifPresent(System.out::println); // 如果值存在,执行操作

3. Collectors 类:流结果的收集器

Collectors 类提供了大量静态方法,用来将 Stream 的结果收集到集合、字符串、统计结果中:

复制代码
List<String> list = Arrays.asList("apple", "banana", "cherry");

// 收集到 List
List<String> resultList = list.stream().collect(Collectors.toList());

// 收集到 Set
Set<String> resultSet = list.stream().collect(Collectors.toSet());

// 拼接字符串
String joined = list.stream().collect(Collectors.joining(","));

// 分组
Map<Integer, List<String>> groupByLength = list.stream()
    .collect(Collectors.groupingBy(String::length));

4. 数据过滤:filter

filter 用于根据条件过滤数据,保留符合条件的元素:

复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream()
    .filter(n -> n % 2 == 0) // 过滤偶数
    .collect(Collectors.toList());
// 结果:[2, 4, 6]

5. 数据映射:map/flatMap

map 用于将流中的元素映射成另一种类型,flatMap 用于处理嵌套集合,将多个流合并成一个流:

复制代码
// map 示例:将字符串转成大写
List<String> words = Arrays.asList("apple", "banana");
List<String> upperWords = words.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());

// flatMap 示例:处理嵌套列表
List<List<Integer>> nestedList = Arrays.asList(Arrays.asList(1,2), Arrays.asList(3,4));
List<Integer> flatList = nestedList.stream()
    .flatMap(List::stream) // 将子列表的流合并成一个流
    .collect(Collectors.toList());
// 结果:[1,2,3,4]

6. 数据查找:find/first/match

Stream 提供了多种查找和匹配的方法:

复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 查找第一个元素
Optional<Integer> first = numbers.stream().findFirst();

// 查找任意元素(并行流中更高效)
Optional<Integer> any = numbers.parallelStream().findAny();

// 判断是否所有元素都满足条件
boolean allEven = numbers.stream().allMatch(n -> n % 2 == 0); // false

// 判断是否存在元素满足条件
boolean hasEven = numbers.stream().anyMatch(n -> n % 2 == 0); // true

7. 数据收集:collect

除了前面提到的 Collectorscollect 还支持自定义收集器,比如统计、分区等:

复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 统计
IntSummaryStatistics stats = numbers.stream()
    .mapToInt(Integer::intValue)
    .summaryStatistics();
System.out.println(stats.getMax()); // 5
System.out.println(stats.getMin()); // 1
System.out.println(stats.getAverage()); // 3.0

// 分区(按奇偶)
Map<Boolean, List<Integer>> partition = numbers.stream()
    .collect(Collectors.partitioningBy(n -> n % 2 == 0));
// 结果:{false=[1,3,5], true=[2,4]}

四、实践与练习:综合案例

我们用一个综合案例,把 Lambda 和 Stream 串起来:

需求:从用户列表中,筛选出年龄大于 18 岁的用户,按年龄排序,收集用户姓名,并用逗号拼接。

复制代码
class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
}

public class StreamPractice {
    public static void main(String[] args) {
        List<User> users = Arrays.asList(
            new User("Alice", 17),
            new User("Bob", 20),
            new User("Charlie", 16),
            new User("David", 22)
        );

        String result = users.stream()
            .filter(user -> user.getAge() > 18) // 过滤年龄>18的用户
            .sorted(Comparator.comparingInt(User::getAge)) // 按年龄排序
            .map(User::getName) // 映射为用户名
            .collect(Collectors.joining(",")); // 拼接字符串

        System.out.println(result); // 输出:Bob,David
    }
}

总结

Lambda 表达式和 Stream 流处理,是 Java 迈向现代编程的重要一步:

  • Lambda 表达式简化了函数式接口的实现,让代码更简洁;
  • 方法引用是 Lambda 的进阶缩写,让代码更易读;
  • Stream 流处理提供了声明式的集合操作方式,支持链式调用和并行处理;
  • Optional 类解决了空指针异常的痛点,让代码更健壮。
相关推荐
是宇写的啊1 小时前
Spring AOP
java·spring
万邦科技Lafite1 小时前
京东item_get接口实战案例:实时商品价格监控全流程解析
java·开发语言·数据库·python·开放api·淘宝开放平台
Cyber4K2 小时前
【Python专项】进阶语法-系统资源监控与数据采集(1)
开发语言·python·php
Mr_pyx2 小时前
Spring AI 入门教程:Java开发者的AI应用捷径
java·人工智能·spring
Le_ee3 小时前
ctfweb:php/php短标签/.haccess+图片马/XXE
开发语言·前端·php
Zephyr_03 小时前
Leedcode算法题
java·算法
苍煜3 小时前
Java开发IO零基础吃透:BIO、NIO、同步异步、阻塞非阻塞
java·python·nio
yong99903 小时前
MATLAB读取高光谱图像
开发语言·matlab
2zcode4 小时前
基于MATLAB的肝病风险评估与分期分析系统设计与实现
开发语言·matlab