🟠 31 Lambda与Stream

📅 更新于 2026年6月 | ✍️ 原创文章,转载请注明出处



1. Lambda表达式

1.1 什么是Lambda

Lambda 是 Java 8 引入的匿名函数写法,让代码更简洁。

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

// Lambda写法
Comparator<String> comp = (a, b) -> a.length() - b.length();

// 更简洁
Comparator<String> comp = Comparator.comparingInt(String::length);

1.2 Lambda语法

(参数列表) -> { 方法体 }
形式示例说明
无参数() -> System.out.println("hi")空括号
单参数x -> x * 2可省略括号
多参数(x, y) -> x + y必须有括号
多行体(x) -> { ... return x; }需要大括号和return
单表达式x -> x + 1自动返回,无需return

1.3 变量作用域

public void test() {
    int num = 10;  // effectively final
    
    Runnable r = () -> {
        // num = 20;  ❌ 不能修改
        System.out.println(num);  // ✅ 可以读取
    };
}

规则:Lambda 只能访问 final 或 effectively final 的局部变量。


2. 函数式接口

2.1 定义

函数式接口:只有一个抽象方法的接口,用 @FunctionalInterface 注解标记。

@FunctionalInterface
public interface MyFunction<T, R> {
    R apply(T t);
    
    // 可以有默认方法
    default void show() {
        System.out.println("default method");
    }
}

2.2 内置函数式接口

接口方法用途示例
Function<T,R>R apply(T)类型转换String::length
Predicate<T>boolean test(T)条件判断s -> s.isEmpty()
Consumer<T>void accept(T)消费数据System.out::println
Supplier<T>T get()生产数据() -> new ArrayList<>()
UnaryOperator<T>T apply(T)一元操作s -> s.toUpperCase()
BinaryOperator<T>T apply(T,T)二元操作Integer::sum
BiFunction<T,U,R>R apply(T,U)双参数转换(a,b) -> a + b
BiPredicate<T,U>boolean test(T,U)双参数判断(a,b) -> a > b

2.3 带类型的函数式接口(基本类型优化)

接口对应泛型用途
IntFunction<R>Function<Integer,R>int参数
IntPredicatePredicate<Integer>int判断
IntConsumerConsumer<Integer>int消费
IntSupplierSupplier<Integer>int生产
LongFunction<R>Function<Long,R>long参数
DoubleFunction<R>Function<Double,R>double参数

3. 方法引用

3.1 四种形式

类型语法Lambda等价
静态方法ClassName::staticMethod(args) -> ClassName.staticMethod(args)
实例方法instance::method(args) -> instance.method(args)
对象方法ClassName::method(obj, args) -> obj.method(args)
构造方法ClassName::new(args) -> new ClassName(args)

3.2 示例

// 静态方法引用
Function<String, Integer> parseInt = Integer::parseInt;
// 等价于: s -> Integer.parseInt(s)

// 实例方法引用
String str = "Hello";
Supplier<Integer> len = str::length;
// 等价于: () -> str.length()

// 对象方法引用(类名)
Function<String, String> upper = String::toUpperCase;
// 等价于: s -> s.toUpperCase()

// 构造方法引用
Supplier<ArrayList<String>> listFactory = ArrayList::new;
// 等价于: () -> new ArrayList<>()

Function<Integer, int[]> arrayFactory = int[]::new;
// 等价于: n -> new int[n]

4. Stream API

4.1 什么是Stream

Stream 是对集合数据的函数式操作管道,不修改原数据。

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

// 传统写法
List<String> result = new ArrayList<>();
for (String name : names) {
    if (name.length() > 3) {
        result.add(name.toUpperCase());
    }
}
Collections.sort(result);

// Stream写法
List<String> result = names.stream()
    .filter(name -> name.length() > 3)
    .map(String::toUpperCase)
    .sorted()
    .collect(Collectors.toList());

4.2 创建Stream

// 从集合
Stream<String> s1 = list.stream();
Stream<String> s2 = list.parallelStream();

// 从数组
Stream<Integer> s3 = Arrays.stream(new Integer[]{1, 2, 3});

// 直接创建
Stream<String> s4 = Stream.of("a", "b", "c");

// 生成
Stream<Integer> s5 = Stream.iterate(0, n -> n + 1);  // 无限流
Stream<Double> s6 = Stream.generate(Math::random);    // 无限流

// 基本类型流
IntStream is = IntStream.range(1, 10);      // 1-9
IntStream is2 = IntStream.rangeClosed(1, 10); // 1-10

4.3 中间操作(惰性求值)

操作方法说明
过滤filter(Predicate)保留满足条件的元素
映射map(Function)转换每个元素
扁平化flatMap(Function)一对多映射并展平
去重distinct()按equals去重
排序sorted()自然排序
排序sorted(Comparator)自定义排序
截取limit(long)取前n个
跳过skip(long)跳过前n个
窥视peek(Consumer)查看每个元素(调试用)

4.4 终端操作

操作方法返回类型
遍历forEach(Consumer)void
转集合collect(Collectors.toList())List
转集合collect(Collectors.toSet())Set
转集合collect(Collectors.toMap(...))Map
数组toArray()Object[]
计数count()long
求和sum() / reduce(0, Integer::sum)int
最大max(Comparator)Optional
最小min(Comparator)Optional
匹配anyMatch(Predicate)boolean
匹配allMatch(Predicate)boolean
匹配noneMatch(Predicate)boolean
查找findFirst()Optional
查找findAny()Optional
归约reduce(BinaryOperator)Optional

5. Stream高级操作

5.1 Collectors 工具类

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

// 分组计数
Map<Integer, Long> countByLen = list.stream()
    .collect(Collectors.groupingBy(String::length, Collectors.counting()));

// 分区(true/false两组)
Map<Boolean, List<String>> partitioned = list.stream()
    .collect(Collectors.partitioningBy(s -> s.length() > 3));

// 连接字符串
String joined = list.stream()
    .collect(Collectors.joining(", ", "[", "]"));
// 输出: [Alice, Bob, Charlie]

// 统计
IntSummaryStatistics stats = intStream.summaryStatistics();
System.out.println("平均: " + stats.getAverage());
System.out.println("最大: " + stats.getMax());
System.out.println("最小: " + stats.getMin());
System.out.println("总和: " + stats.getSum());
System.out.println("数量: " + stats.getCount());

5.2 flatMap 示例

// 一对多映射并展平
List<List<Integer>> nested = Arrays.asList(
    Arrays.asList(1, 2, 3),
    Arrays.asList(4, 5),
    Arrays.asList(6, 7, 8, 9)
);

List<Integer> flat = nested.stream()
    .flatMap(Collection::stream)
    .collect(Collectors.toList());
// 结果: [1, 2, 3, 4, 5, 6, 7, 8, 9]

// 拆分单词
List<String> sentences = Arrays.asList("Hello World", "Java Stream");
List<String> words = sentences.stream()
    .flatMap(s -> Arrays.stream(s.split(" ")))
    .collect(Collectors.toList());
// 结果: [Hello, World, Java, Stream]

5.3 reduce 归约

// 求和
int sum = IntStream.rangeClosed(1, 100)
    .reduce(0, Integer::sum);
// 结果: 5050

// 求最大值
int max = IntStream.of(3, 1, 4, 1, 5, 9)
    .reduce(Integer::max)
    .orElse(0);
// 结果: 9

// 字符串拼接
String result = Stream.of("A", "B", "C")
    .reduce("", (a, b) -> a.isEmpty() ? b : a + "," + b);
// 结果: A,B,C

6. 并行流

6.1 基本使用

// 串行流
long count1 = list.stream()
    .filter(s -> s.length() > 3)
    .count();

// 并行流
long count2 = list.parallelStream()
    .filter(s -> s.length() > 3)
    .count();

6.2 注意事项

事项说明
线程安全操作必须是无状态的
顺序并行流不保证顺序,如需顺序用 forEachOrdered
数据量小数据量并行反而更慢(线程开销)
适用场景CPU密集型、大数据量
不适用涉及IO、需要顺序、小数据集
// 错误示例:共享可变状态
List<String> result = new ArrayList<>();
list.parallelStream()
    .filter(s -> s.length() > 3)
    .forEach(s -> result.add(s));  // ❌ 线程不安全!

// 正确做法
List<String> result = list.parallelStream()
    .filter(s -> s.length() > 3)
    .collect(Collectors.toList());  // ✅ collect是线程安全的

7. 实战案例

7.1 案例:学生信息处理

@Data
class Student {
    private String name;
    private int age;
    private double score;
    private String city;
}

public class StreamDemo {
    public static void main(String[] args) {
        List<Student> students = Arrays.asList(
            new Student("Alice", 20, 92.5, "北京"),
            new Student("Bob", 22, 78.0, "上海"),
            new Student("Charlie", 21, 85.5, "北京"),
            new Student("David", 23, 96.0, "广州"),
            new Student("Eve", 20, 88.5, "上海")
        );
        
        // 1. 按城市分组
        Map<String, List<Student>> byCity = students.stream()
            .collect(Collectors.groupingBy(Student::getCity));
        
        // 2. 各城市平均分
        Map<String, Double> avgByCity = students.stream()
            .collect(Collectors.groupingBy(
                Student::getCity,
                Collectors.averagingDouble(Student::getScore)
            ));
        
        // 3. 成绩前3名
        List<String> top3 = students.stream()
            .sorted(Comparator.comparingDouble(Student::getScore).reversed())
            .limit(3)
            .map(Student::getName)
            .collect(Collectors.toList());
        
        // 4. 是否有不及格学生
        boolean hasFailed = students.stream()
            .anyMatch(s -> s.getScore() < 60);
        
        // 5. 统计信息
        DoubleSummaryStatistics stats = students.stream()
            .mapToDouble(Student::getScore)
            .summaryStatistics();
        
        System.out.println("按城市分组: " + byCity);
        System.out.println("各城市平均分: " + avgByCity);
        System.out.println("前3名: " + top3);
        System.out.println("有不及格: " + hasFailed);
        System.out.println("最高分: " + stats.getMax());
        System.out.println("平均分: " + stats.getAverage());
    }
}

8. 总结

特性说明
Lambda匿名函数,简化代码
函数式接口只有一个抽象方法的接口
方法引用更简洁的Lambda写法
Stream集合的函数式操作管道
惰性求值中间操作不执行,终端操作才触发
并行流大数据量下利用多核CPU

💬 你觉得Lambda和Stream让代码更易读还是更难读?在什么场景下你会优先使用Stream?

📌 下一篇我们将学习 Optional与Java新API,告别空指针异常!


📚 参考资料

更多推荐