前置知识: Java

Java函数式编程

00:00
3 min Intermediate 2026/6/14

Lambda、Stream与函数式接口

概述

Java 8 引入的 Lambda 表达式Stream API 标志着 Java 迈入函数式编程时代。函数式编程强调将行为参数化、使用不可变数据和声明式风格,使代码更简洁、更易并行化。本文介绍 Java 函数式编程的核心概念和实用技巧。

基础概念

函数式接口

接口签名用途
Function<T, R>T -> R转换数据
ConsumerT -> void消费数据
Supplier() -> T提供数据
PredicateT -> boolean判断条件
BiFunction<T,U,R>(T, U) -> R参数转换
UnaryOperatorT -> T操作
BinaryOperator(T, T) -> T操作

核心原则

  • 函数是一等公民,可以作为参数传递返回值
  • 避免可变状态,优先使用不可变对象
  • 声明式编程描述”做什么”而非”怎么做”
  • 纯函数:相同输入始终产生相同输出,无副作用

快速上手

Lambda 表达式

// 无参数
Runnable r = () -> System.out.println("Hello");

// 单参数(类型推断)
Consumer<String> print = s -> System.out.println(s);

// 多参数
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;

// 代码块
Comparator<String> cmp = (s1, s2) -> {
    int diff = s1.length() - s2.length();
    return diff != 0 ? diff : s1.compareTo(s2);
};

// 方法引用
Consumer<String> printRef = System.out::println;     // 实例方法引用
Function<String, Integer> len = String::length;       // 实例方法引用
Supplier<List<String>> listFactory = ArrayList::new;  // 构造方法引用

自定义函数式接口

// 自定义函数式接口
@FunctionalInterface
interface Transformer<T, R> {
    R transform(T input);

    // 可以有默认方法
    default <V> Transformer<T, V> andThen(Function<R, V> after) {
        return input -> after.apply(transform(input));
    }
}

// 使用自定义接口
Transformer<String, Integer> strLen = String::length;
Transformer<String, String> upper = String::toUpperCase;
Transformer<String, String> composed = strLen.andThen(n -> "长度: " + n);

详细用法

Stream 基本操作

// 创建 Stream
Stream<String> s1 = Stream.of("a", "b", "c");
Stream<Integer> s2 = IntStream.range(1, 100).boxed();
Stream<Double> s3 = Stream.generate(Math::random).limit(10);

// 中间操作(惰性求值)
List<String> names = users.stream()
    .filter(u -> u.getAge() > 18)        // 过滤
    .map(User::getName)                   // 映射
    .flatMap(name -> Arrays.stream(name.split(" "))) // 展平
    .distinct()                           // 去重
    .sorted()                             // 排序
    .peek(System.out::println)            // 调试(不影响流)
    .skip(10)                             // 跳过前10个
    .limit(20)                            // 最多取20个
    .collect(Collectors.toList());        // 终端操作

Collectors 收集器

// 常用收集器
// 转为列表
List<String> list = stream.collect(Collectors.toList());

// 转为字符串
String joined = names.stream().collect(Collectors.joining(", "));

// 分组
Map<String, List<User>> byCity = users.stream()
    .collect(Collectors.groupingBy(User::getCity));

// 分区(按条件分为 true/false 两组)
Map<Boolean, List<User>> partitioned = users.stream()
    .collect(Collectors.partitioningBy(u -> u.getAge() >= 18));

// 下游收集器:分组后统计
Map<String, Long> countByCity = users.stream()
    .collect(Collectors.groupingBy(User::getCity, Collectors.counting()));

// 分组后取最大值
Map<String, Optional<User>> oldestByCity = users.stream()
    .collect(Collectors.groupingBy(User::getCity,
        Collectors.maxBy(Comparator.comparingInt(User::getAge))));

// 收集为不可变列表
List<String> unmodifiable = stream.collect(
    Collectors.toUnmodifiableList());

原始类型流

// 使用 IntStream/LongStream/DoubleStream 避免装箱开销
IntSummaryStatistics stats = users.stream()
    .mapToInt(User::getAge)
    .summaryStatistics();

System.out.println("平均年龄: " + stats.getAverage());
System.out.println("最大年龄: " + stats.getMax());
System.out.println("总人数: " + stats.getCount());

// 数值范围操作
long evenCount = IntStream.rangeClosed(1, 100)
    .filter(n -> n % 2 == 0)
    .count();

// 数值聚合
double average = users.stream()
    .mapToInt(User::getAge)
    .average()
    .orElse(0.0);

常见场景

数据转换管道

// 将原始数据转换为业务对象
List<OrderDTO> dtos = orders.stream()
    .filter(order -> order.getStatus() == Status.COMPLETED)
    .map(order -> {
        OrderDTO dto = new OrderDTO();
        dto.setOrderId(order.getId());
        dto.setCustomerName(order.getCustomer().getName());
        dto.setTotal(order.getItems().stream()
            .mapToDouble(Item::getPrice)
            .sum());
        dto.setItemCount(order.getItems().size());
        return dto;
    })
    .sorted(Comparator.comparingDouble(OrderDTO::getTotal).reversed())
    .collect(Collectors.toList());

并行流

// 使用并行流处理大数据集
long count = bigList.parallelStream()
    .filter(item -> isValid(item))
    .map(item -> transform(item))
    .count();

// 注意:并行流适合 CPU 密集型且无状态的操作
// 自定义并行度
ForkJoinPool customPool = new ForkJoinPool(4);
List<Result> results = customPool.submit(() ->
    bigList.parallelStream()
        .map(this::process)
        .collect(Collectors.toList())
).get();

注意事项

  • Stream 只能消费一次,重复使用会抛出 IllegalStateException
  • 并行流不保证元素顺序,除非使用 forEachOrdered
  • 避免在 Lambda 中修改外部可变状态,这会导致难以调试的并发问题
  • Stream 操作中的异常需要处理受检异常需要包装非受检异常
  • 短路操作(findAny、anyMatch)可以提前终止流,提升性能
  • 简单传统 for 循环Stream 更易读且性能更好

进阶用法

自定义收集器

// 实现自定义收集器:将流元素收集为以逗号分隔的字符串
public class StringJoiningCollector implements Collector<String, StringBuilder, String> {
    private final String delimiter;

    public StringJoiningCollector(String delimiter) {
        this.delimiter = delimiter;
    }

    @Override
    public Supplier<StringBuilder> supplier() {
        return StringBuilder::new;
    }

    @Override
    public BiConsumer<StringBuilder, String> accumulator() {
        return (sb, s) -> {
            if (sb.length() > 0) sb.append(delimiter);
            sb.append(s);
        };
    }

    @Override
    public BinaryOperator<StringBuilder> combiner() {
        return (sb1, sb2) -> sb1.append(delimiter).append(sb2);
    }

    @Override
    public Function<StringBuilder, String> finisher() {
        return StringBuilder::toString;
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Set.of();
    }
}

函数组合与柯里化

// 函数组合
Function<Integer, Integer> doubleIt = x -> x * 2;
Function<Integer, Integer> addOne = x -> x + 1;

// compose: 先执行参数函数,再执行当前函数
Function<Integer, Integer> doubleThenAdd = addOne.compose(doubleIt); // x*2+1

// andThen: 先执行当前函数,再执行参数函数
Function<Integer, Integer> addThenDouble = doubleIt.andThen(addOne); // (x+1)*2

// 柯里化:将多参数函数转换为一系列单参数函数
BiFunction<Integer, Integer, Integer> multiply = (a, b) -> a * b;
Function<Integer, Function<Integer, Integer>> curriedMultiply =
    a -> b -> a * b;

// 部分应用
Function<Integer, Integer> triple = curriedMultiply.apply(3);
System.out.println(triple.apply(5)); // 15

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式