Stream API与Lambda表达式实战

0 阅读34分钟

Stream API与Lambda表达式实战

学习目标

  • 理解Lambda表达式的本质和使用场景
  • 掌握函数式接口的概念和常用接口
  • 熟练使用Stream API进行集合操作
  • 能够使用Stream API解决实际业务问题
  • 理解Stream的惰性求值和并行流
  • 掌握方法引用的使用技巧

一、为什么需要Lambda和Stream?

1.1 传统代码的痛点

场景:过滤集合中的数据

// 传统写法:过滤年龄大于18岁的用户
List<User> users = getUserList();
List<User> adults = new ArrayList<>();
for (User user : users) {
    if (user.getAge() > 18) {
        adults.add(user);
    }
}

// 问题:
// 1. 代码冗长,样板代码多
// 2. 不够直观,需要理解循环逻辑
// 3. 难以并行化处理
// 4. 多个操作需要多次循环

使用Lambda和Stream后:

// 现代写法
List<User> adults = users.stream()
    .filter(user -> user.getAge() > 18)
    .collect(Collectors.toList());

// 优势:
// 1. 代码简洁,意图清晰
// 2. 声明式编程,关注"做什么"而非"怎么做"
// 3. 易于并行化(只需改为parallelStream())
// 4. 可以链式调用多个操作

1.2 核心思想

Lambda表达式 = 匿名函数 = 行为参数化
Stream API = 声明式数据处理 = 函数式编程

为什么要这样设计?

  • 代码简洁:减少样板代码,提高开发效率
  • 可读性强:代码更接近自然语言描述
  • 易于维护:业务逻辑更清晰,修改更容易
  • 性能优化:支持惰性求值和并行处理

二、Lambda表达式详解

2.1 Lambda语法

// 基本语法
(参数列表) -> { 方法体 }

// 语法变体
() -> 表达式                    // 无参数
(参数) -> 表达式                // 单参数
(参数1, 参数2) -> 表达式        // 多参数
(参数) -> { 语句1; 语句2; }     // 多行代码块

实际示例:

// 1. 无参数Lambda
Runnable r = () -> System.out.println("Hello");
//           ^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^
//           │              └── 方法体
//           └── 无参数

// 2. 单参数Lambda(可省略括号)
Consumer<String> c1 = (s) -> System.out.println(s);
Consumer<String> c2 = s -> System.out.println(s);  // 省略括号
//                    ^
//                    └── 单参数可省略括号

// 3. 多参数Lambda
Comparator<Integer> comp = (a, b) -> a.compareTo(b);
//                         ^^^^^^    ^^^^^^^^^^^^^^^^
//                           │              └── 比较逻辑
//                           └── 两个参数

// 4. 多行代码块
BiFunction<Integer, Integer, Integer> calc = (a, b) -> {
    int sum = a + b;
    int result = sum * 2;
    return result;
};

2.2 函数式接口

什么是函数式接口?

// 函数式接口 = 只有一个抽象方法的接口
@FunctionalInterface  // 可选注解,用于编译检查
public interface MyFunction {
    void doSomething();  // 唯一的抽象方法
    
    // 可以有默认方法
    default void defaultMethod() {
        System.out.println("默认方法");
    }
    
    // 可以有静态方法
    static void staticMethod() {
        System.out.println("静态方法");
    }
}

为什么需要函数式接口?

  • Lambda表达式需要一个"目标类型"
  • 函数式接口就是Lambda的目标类型
  • 编译器通过接口的抽象方法签名推断Lambda的参数和返回值类型

2.3 Java内置函数式接口

┌─────────────────────────────────────────────────────────────────┐
│                    Java内置函数式接口                            │
├─────────────────────────────────────────────────────────────────┤
│ 接口名              方法签名              用途                   │
├─────────────────────────────────────────────────────────────────┤
│ Consumer<T>        void accept(T t)      消费一个参数,无返回值  │
│ Supplier<T>        T get()               无参数,返回一个结果    │
│ Function<T,R>      R apply(T t)          接收T,返回R           │
│ Predicate<T>       boolean test(T t)     接收T,返回布尔值      │
│ BiFunction<T,U,R>  R apply(T t, U u)     接收两个参数,返回R    │
│ UnaryOperator<T>   T apply(T t)          接收T,返回T(特殊化) │
│ BinaryOperator<T>  T apply(T t1, T t2)   接收两个T,返回T       │
└─────────────────────────────────────────────────────────────────┘

实际使用示例:

// 1. Consumer - 消费数据
Consumer<String> printer = s -> System.out.println(s);
printer.accept("Hello");  // 输出: Hello

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));  // forEach接收Consumer

// 2. Supplier - 提供数据
Supplier<String> supplier = () -> "Hello World";
String result = supplier.get();  // 获取数据

// 延迟计算示例
Supplier<Double> randomSupplier = () -> Math.random();
Double num1 = randomSupplier.get();  // 每次调用生成新的随机数
Double num2 = randomSupplier.get();

// 3. Function - 转换数据
Function<String, Integer> strLength = s -> s.length();
Integer length = strLength.apply("Hello");  // 5

// 链式调用
Function<String, String> addPrefix = s -> "Mr. " + s;
Function<String, String> addSuffix = s -> s + " Jr.";
Function<String, String> combined = addPrefix.andThen(addSuffix);
String name = combined.apply("Smith");  // "Mr. Smith Jr."

// 4. Predicate - 判断条件
Predicate<Integer> isEven = n -> n % 2 == 0;
boolean result1 = isEven.test(4);  // true
boolean result2 = isEven.test(5);  // false

// 组合条件
Predicate<Integer> isPositive = n -> n > 0;
Predicate<Integer> isPositiveEven = isPositive.and(isEven);
boolean result3 = isPositiveEven.test(4);  // true

// 5. BiFunction - 两个参数的函数
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
Integer sum = add.apply(3, 5);  // 8

// 6. UnaryOperator - 输入输出类型相同
UnaryOperator<String> toUpper = s -> s.toUpperCase();
String upper = toUpper.apply("hello");  // "HELLO"

// 7. BinaryOperator - 两个相同类型参数
BinaryOperator<Integer> max = (a, b) -> a > b ? a : b;
Integer maxValue = max.apply(10, 20);  // 20

2.4 方法引用

什么是方法引用?

方法引用 = Lambda表达式的简写形式
当Lambda只是调用一个已存在的方法时,可以使用方法引用

四种方法引用类型:

// 1. 静态方法引用:类名::静态方法名
Function<String, Integer> parser1 = s -> Integer.parseInt(s);
Function<String, Integer> parser2 = Integer::parseInt;  // 方法引用
//                                   ^^^^^^^  ^^^^^^^^
//                                     类名    静态方法

// 2. 实例方法引用:对象::实例方法名
String str = "Hello";
Supplier<String> supplier1 = () -> str.toUpperCase();
Supplier<String> supplier2 = str::toUpperCase;  // 方法引用
//                           ^^^  ^^^^^^^^^^^
//                           对象   实例方法

// 3. 类的实例方法引用:类名::实例方法名
Function<String, String> func1 = s -> s.toUpperCase();
Function<String, String> func2 = String::toUpperCase;  // 方法引用
//                               ^^^^^^  ^^^^^^^^^^^
//                                类名    实例方法
// 注意:第一个参数会成为方法的调用者

// 4. 构造方法引用:类名::new
Supplier<List<String>> listSupplier1 = () -> new ArrayList<>();
Supplier<List<String>> listSupplier2 = ArrayList::new;  // 构造方法引用
//                                     ^^^^^^^^^  ^^^
//                                       类名     new

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

实际应用场景:

// 场景1:集合排序
List<String> names = Arrays.asList("Charlie", "Alice", "Bob");

// Lambda写法
names.sort((a, b) -> a.compareTo(b));

// 方法引用写法
names.sort(String::compareTo);

// 场景2:集合遍历
List<String> list = Arrays.asList("A", "B", "C");

// Lambda写法
list.forEach(s -> System.out.println(s));

// 方法引用写法
list.forEach(System.out::println);

// 场景3:对象创建
List<String> strings = Arrays.asList("1", "2", "3");

// Lambda写法
List<Integer> numbers1 = strings.stream()
    .map(s -> Integer.parseInt(s))
    .collect(Collectors.toList());

// 方法引用写法
List<Integer> numbers2 = strings.stream()
    .map(Integer::parseInt)
    .collect(Collectors.toList());

// 场景4:对象方法调用
List<User> users = getUserList();

// Lambda写法
List<String> names1 = users.stream()
    .map(user -> user.getName())
    .collect(Collectors.toList());

// 方法引用写法
List<String> names2 = users.stream()
    .map(User::getName)
    .collect(Collectors.toList());

为什么使用方法引用?

  • 代码更简洁
  • 可读性更好
  • 减少重复代码

三、Stream API核心概念

3.1 什么是Stream?

Stream = 数据流 = 对集合进行声明式操作的抽象

特点:
1. 不存储数据(只是数据源的视图)
2. 不修改数据源(函数式编程,不可变)
3. 惰性求值(只有终止操作才会执行)
4. 可以无限(支持无限流)

Stream vs 集合:

集合(Collection)              Stream
┌─────────────────┐           ┌─────────────────┐
│ 存储数据         │           │ 不存储数据       │
│ 外部迭代         │           │ 内部迭代         │
│ 可以多次遍历     │           │ 只能遍历一次     │
│ 主动获取数据     │           │ 按需计算         │
└─────────────────┘           └─────────────────┘

3.2 Stream操作分类

Stream操作
    │
    ├── 中间操作(Intermediate Operations)
    │   ├── 无状态操作:filter、map、flatMap、peek
    │   └── 有状态操作:distinct、sorted、limit、skip
    │
    └── 终止操作(Terminal Operations)
        ├── 非短路操作:forEach、collect、reduce、count
        └── 短路操作:anyMatch、allMatch、findFirst、findAny

中间操作 vs 终止操作:

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

// 只有中间操作,不会执行
Stream<Integer> stream = numbers.stream()
    .filter(n -> {
        System.out.println("filter: " + n);
        return n > 2;
    })
    .map(n -> {
        System.out.println("map: " + n);
        return n * 2;
    });
// 此时不会有任何输出!

// 添加终止操作后才会执行
List<Integer> result = stream.collect(Collectors.toList());
// 输出:
// filter: 1
// filter: 2
// filter: 3
// map: 3
// filter: 4
// map: 4
// filter: 5
// map: 5

为什么要惰性求值?

  • 性能优化:避免不必要的计算
  • 支持无限流:可以处理无限数据源
  • 短路操作:找到结果后立即停止

3.3 创建Stream

// 1. 从集合创建
List<String> list = Arrays.asList("A", "B", "C");
Stream<String> stream1 = list.stream();
Stream<String> parallelStream = list.parallelStream();  // 并行流

// 2. 从数组创建
String[] array = {"A", "B", "C"};
Stream<String> stream2 = Arrays.stream(array);
Stream<String> stream3 = Stream.of(array);

// 3. 直接创建
Stream<String> stream4 = Stream.of("A", "B", "C");
Stream<Integer> stream5 = Stream.of(1, 2, 3, 4, 5);

// 4. 创建空流
Stream<String> emptyStream = Stream.empty();

// 5. 创建无限流
// generate:生成无限流
Stream<Double> randomStream = Stream.generate(Math::random);
Stream<String> constantStream = Stream.generate(() -> "Hello");

// iterate:迭代生成
Stream<Integer> evenNumbers = Stream.iterate(0, n -> n + 2);
//                                           ^初始值  ^迭代函数
// 生成:0, 2, 4, 6, 8, 10, ...

// Java 9+:带终止条件的iterate
Stream<Integer> limitedStream = Stream.iterate(0, n -> n < 100, n -> n + 2);
//                                                 ^终止条件

// 6. 从文件创建
try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {
    lines.forEach(System.out::println);
}

// 7. 从字符串创建
IntStream chars = "Hello".chars();  // 字符流
Stream<String> words = Pattern.compile("\\s+").splitAsStream("Hello World");

// 8. 数值流
IntStream intStream = IntStream.range(1, 10);      // 1到9
IntStream intStream2 = IntStream.rangeClosed(1, 10);  // 1到10
LongStream longStream = LongStream.range(1L, 100L);
DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0);

四、Stream中间操作详解

4.1 filter - 过滤

// 语法
Stream<T> filter(Predicate<? super T> predicate)

// 作用:保留满足条件的元素

实际示例:

List<User> users = Arrays.asList(
    new User("Alice", 25, "女"),
    new User("Bob", 30, "男"),
    new User("Charlie", 20, "男"),
    new User("Diana", 28, "女")
);

// 1. 简单过滤
List<User> adults = users.stream()
    .filter(user -> user.getAge() >= 25)
    .collect(Collectors.toList());

// 2. 多条件过滤
List<User> result = users.stream()
    .filter(user -> user.getAge() >= 25)
    .filter(user -> "女".equals(user.getGender()))
    .collect(Collectors.toList());
// 结果:Alice(25,女), Diana(28,女)

// 3. 复杂条件过滤
List<User> result2 = users.stream()
    .filter(user -> {
        boolean ageCondition = user.getAge() >= 25 && user.getAge() <= 30;
        boolean genderCondition = "女".equals(user.getGender());
        return ageCondition && genderCondition;
    })
    .collect(Collectors.toList());

// 4. 使用Predicate组合
Predicate<User> isAdult = user -> user.getAge() >= 25;
Predicate<User> isFemale = user -> "女".equals(user.getGender());
Predicate<User> combined = isAdult.and(isFemale);

List<User> result3 = users.stream()
    .filter(combined)
    .collect(Collectors.toList());

// 5. 过滤null值
List<String> names = Arrays.asList("Alice", null, "Bob", null, "Charlie");
List<String> nonNullNames = names.stream()
    .filter(Objects::nonNull)
    .collect(Collectors.toList());
// 结果:["Alice", "Bob", "Charlie"]

4.2 map - 映射转换

// 语法
<R> Stream<R> map(Function<? super T, ? extends R> mapper)

// 作用:将每个元素转换为另一种类型

实际示例:

List<User> users = getUserList();

// 1. 提取属性
List<String> names = users.stream()
    .map(User::getName)
    .collect(Collectors.toList());

List<Integer> ages = users.stream()
    .map(User::getAge)
    .collect(Collectors.toList());

// 2. 类型转换
List<String> numbers = Arrays.asList("1", "2", "3", "4", "5");
List<Integer> integers = numbers.stream()
    .map(Integer::parseInt)
    .collect(Collectors.toList());

// 3. 对象转换
List<UserDTO> dtos = users.stream()
    .map(user -> {
        UserDTO dto = new UserDTO();
        dto.setName(user.getName());
        dto.setAge(user.getAge());
        return dto;
    })
    .collect(Collectors.toList());

// 使用方法引用简化
List<UserDTO> dtos2 = users.stream()
    .map(UserDTO::fromUser)  // 假设UserDTO有静态方法fromUser
    .collect(Collectors.toList());

// 4. 字符串操作
List<String> words = Arrays.asList("hello", "world", "java");
List<String> upperWords = words.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());
// 结果:["HELLO", "WORLD", "JAVA"]

// 5. 计算转换
List<Integer> numbers2 = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squares = numbers2.stream()
    .map(n -> n * n)
    .collect(Collectors.toList());
// 结果:[1, 4, 9, 16, 25]

// 6. 链式map
List<String> result = users.stream()
    .map(User::getName)           // User -> String
    .map(String::toUpperCase)     // String -> String
    .map(s -> "Mr. " + s)         // String -> String
    .collect(Collectors.toList());

4.3 flatMap - 扁平化映射

// 语法
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)

// 作用:将每个元素转换为Stream,然后合并所有Stream

map vs flatMap:

map:      [[1,2], [3,4]]  ->  [Stream[1,2], Stream[3,4]]
flatMap:  [[1,2], [3,4]]  ->  [1, 2, 3, 4]
          ^^^^^^^^              ^^^^^^^^^^^
          嵌套结构              扁平结构

实际示例:

// 1. 扁平化嵌套集合
List<List<Integer>> nestedList = Arrays.asList(
    Arrays.asList(1, 2, 3),
    Arrays.asList(4, 5),
    Arrays.asList(6, 7, 8, 9)
);

// 使用map(错误示范)
List<Stream<Integer>> wrongResult = nestedList.stream()
    .map(list -> list.stream())
    .collect(Collectors.toList());
// 结果:[Stream, Stream, Stream] - 还是嵌套的!

// 使用flatMap(正确)
List<Integer> flatList = nestedList.stream()
    .flatMap(list -> list.stream())
    .collect(Collectors.toList());
// 结果:[1, 2, 3, 4, 5, 6, 7, 8, 9] - 扁平化了!

// 2. 字符串拆分
List<String> sentences = Arrays.asList(
    "Hello World",
    "Java Stream",
    "Lambda Expression"
);

// 获取所有单词
List<String> words = sentences.stream()
    .flatMap(sentence -> Arrays.stream(sentence.split(" ")))
    .collect(Collectors.toList());
// 结果:["Hello", "World", "Java", "Stream", "Lambda", "Expression"]

// 3. 对象属性扁平化
class Order {
    private List<OrderItem> items;
    // getter/setter
}

List<Order> orders = getOrders();

// 获取所有订单项
List<OrderItem> allItems = orders.stream()
    .flatMap(order -> order.getItems().stream())
    .collect(Collectors.toList());

// 4. 字符串转字符
List<String> words2 = Arrays.asList("Hello", "World");
List<String> chars = words2.stream()
    .flatMap(word -> Arrays.stream(word.split("")))
    .collect(Collectors.toList());
// 结果:["H", "e", "l", "l", "o", "W", "o", "r", "l", "d"]

// 去重
List<String> uniqueChars = words2.stream()
    .flatMap(word -> Arrays.stream(word.split("")))
    .distinct()
    .collect(Collectors.toList());
// 结果:["H", "e", "l", "o", "W", "r", "d"]

// 5. Optional扁平化
List<Optional<String>> optionals = Arrays.asList(
    Optional.of("A"),
    Optional.empty(),
    Optional.of("B"),
    Optional.empty(),
    Optional.of("C")
);

List<String> values = optionals.stream()
    .flatMap(Optional::stream)  // Java 9+
    .collect(Collectors.toList());
// 结果:["A", "B", "C"]

// Java 8兼容写法
List<String> values2 = optionals.stream()
    .filter(Optional::isPresent)
    .map(Optional::get)
    .collect(Collectors.toList());

4.4 distinct - 去重

// 语法
Stream<T> distinct()

// 作用:去除重复元素(基于equals和hashCode)

实际示例:

// 1. 基本类型去重
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4, 5, 5);
List<Integer> uniqueNumbers = numbers.stream()
    .distinct()
    .collect(Collectors.toList());
// 结果:[1, 2, 3, 4, 5]

// 2. 字符串去重
List<String> words = Arrays.asList("apple", "banana", "apple", "cherry", "banana");
List<String> uniqueWords = words.stream()
    .distinct()
    .collect(Collectors.toList());
// 结果:["apple", "banana", "cherry"]

// 3. 对象去重(需要重写equals和hashCode)
class User {
    private String name;
    private int age;
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age && Objects.equals(name, user.name);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

List<User> users = Arrays.asList(
    new User("Alice", 25),
    new User("Bob", 30),
    new User("Alice", 25),  // 重复
    new User("Charlie", 20)
);

List<User> uniqueUsers = users.stream()
    .distinct()
    .collect(Collectors.toList());
// 结果:3个用户(去除了重复的Alice)

// 4. 按属性去重(使用TreeSet)
List<User> users2 = getUserList();
List<User> uniqueByName = users2.stream()
    .collect(Collectors.collectingAndThen(
        Collectors.toCollection(() -> 
            new TreeSet<>(Comparator.comparing(User::getName))),
        ArrayList::new
    ));

// 5. 按属性去重(使用Map)
List<User> uniqueByName2 = new ArrayList<>(
    users2.stream()
        .collect(Collectors.toMap(
            User::getName,
            user -> user,
            (existing, replacement) -> existing  // 保留第一个
        ))
        .values()
);

4.5 sorted - 排序

// 语法
Stream<T> sorted()  // 自然排序
Stream<T> sorted(Comparator<? super T> comparator)  // 自定义排序

// 作用:对流中的元素进行排序

实际示例:

// 1. 自然排序
List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9, 3);
List<Integer> sorted = numbers.stream()
    .sorted()
    .collect(Collectors.toList());
// 结果:[1, 2, 3, 5, 8, 9]

List<String> words = Arrays.asList("banana", "apple", "cherry");
List<String> sortedWords = words.stream()
    .sorted()
    .collect(Collectors.toList());
// 结果:["apple", "banana", "cherry"]

// 2. 自定义排序
List<User> users = getUserList();

// 按年龄升序
List<User> sortedByAge = users.stream()
    .sorted(Comparator.comparing(User::getAge))
    .collect(Collectors.toList());

// 按年龄降序
List<User> sortedByAgeDesc = users.stream()
    .sorted(Comparator.comparing(User::getAge).reversed())
    .collect(Collectors.toList());

// 按姓名排序
List<User> sortedByName = users.stream()
    .sorted(Comparator.comparing(User::getName))
    .collect(Collectors.toList());

// 3. 多字段排序
// 先按年龄升序,年龄相同按姓名升序
List<User> multiSorted = users.stream()
    .sorted(Comparator.comparing(User::getAge)
                      .thenComparing(User::getName))
    .collect(Collectors.toList());

// 先按年龄降序,年龄相同按姓名升序
List<User> multiSorted2 = users.stream()
    .sorted(Comparator.comparing(User::getAge).reversed()
                      .thenComparing(User::getName))
    .collect(Collectors.toList());

// 4. 空值处理
List<User> usersWithNull = Arrays.asList(
    new User("Alice", 25),
    new User(null, 30),
    new User("Bob", 20)
);

// null值排在最前
List<User> nullsFirst = usersWithNull.stream()
    .sorted(Comparator.comparing(User::getName, 
                                 Comparator.nullsFirst(String::compareTo)))
    .collect(Collectors.toList());

// null值排在最后
List<User> nullsLast = usersWithNull.stream()
    .sorted(Comparator.comparing(User::getName,
                                 Comparator.nullsLast(String::compareTo)))
    .collect(Collectors.toList());

// 5. 自定义比较逻辑
List<String> strings = Arrays.asList("a1", "a10", "a2", "a20");

// 按字符串长度排序
List<String> sortedByLength = strings.stream()
    .sorted(Comparator.comparingInt(String::length))
    .collect(Collectors.toList());

// 自定义比较器
List<String> customSorted = strings.stream()
    .sorted((s1, s2) -> {
        // 提取数字部分比较
        int num1 = Integer.parseInt(s1.substring(1));
        int num2 = Integer.parseInt(s2.substring(1));
        return Integer.compare(num1, num2);
    })
    .collect(Collectors.toList());
// 结果:["a1", "a2", "a10", "a20"]

4.6 limit和skip - 截取

// 语法
Stream<T> limit(long maxSize)  // 保留前n个元素
Stream<T> skip(long n)         // 跳过前n个元素

// 作用:截取流的一部分

实际示例:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// 1. limit - 取前n个
List<Integer> first5 = numbers.stream()
    .limit(5)
    .collect(Collectors.toList());
// 结果:[1, 2, 3, 4, 5]

// 2. skip - 跳过前n个
List<Integer> skipFirst5 = numbers.stream()
    .skip(5)
    .collect(Collectors.toList());
// 结果:[6, 7, 8, 9, 10]

// 3. 组合使用 - 分页
int pageSize = 3;
int pageNum = 2;  // 第2页(从0开始)

List<Integer> page = numbers.stream()
    .skip(pageNum * pageSize)  // 跳过前面的页
    .limit(pageSize)           // 取当前页的数据
    .collect(Collectors.toList());
// 结果:[7, 8, 9]

// 4. 实际分页示例
public class PageResult<T> {
    private List<T> data;
    private int pageNum;
    private int pageSize;
    private long total;
    
    public static <T> PageResult<T> of(List<T> allData, int pageNum, int pageSize) {
        List<T> pageData = allData.stream()
            .skip((long) pageNum * pageSize)
            .limit(pageSize)
            .collect(Collectors.toList());
        
        PageResult<T> result = new PageResult<>();
        result.setData(pageData);
        result.setPageNum(pageNum);
        result.setPageSize(pageSize);
        result.setTotal(allData.size());
        return result;
    }
}

// 使用
List<User> allUsers = getUserList();
PageResult<User> page1 = PageResult.of(allUsers, 0, 10);  // 第1页
PageResult<User> page2 = PageResult.of(allUsers, 1, 10);  // 第2页

// 5. 取前n个最大值
List<Integer> top3 = numbers.stream()
    .sorted(Comparator.reverseOrder())  // 降序排序
    .limit(3)                           // 取前3个
    .collect(Collectors.toList());
// 结果:[10, 9, 8]

// 6. 跳过前n个最小值
List<Integer> skipMin3 = numbers.stream()
    .sorted()      // 升序排序
    .skip(3)       // 跳过前3个最小值
    .collect(Collectors.toList());
// 结果:[4, 5, 6, 7, 8, 9, 10]

// 7. 无限流中使用limit
List<Integer> first10Even = Stream.iterate(0, n -> n + 2)
    .limit(10)
    .collect(Collectors.toList());
// 结果:[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

4.7 peek - 调试查看

// 语法
Stream<T> peek(Consumer<? super T> action)

// 作用:对每个元素执行操作,但不改变流(主要用于调试)

实际示例:

// 1. 调试查看中间结果
List<Integer> result = Stream.of(1, 2, 3, 4, 5)
    .peek(n -> System.out.println("原始值: " + n))
    .filter(n -> n > 2)
    .peek(n -> System.out.println("过滤后: " + n))
    .map(n -> n * 2)
    .peek(n -> System.out.println("映射后: " + n))
    .collect(Collectors.toList());

// 输出:
// 原始值: 1
// 原始值: 2
// 原始值: 3
// 过滤后: 3
// 映射后: 6
// 原始值: 4
// 过滤后: 4
// 映射后: 8
// ...

// 2. 修改对象属性(不推荐,违反函数式编程原则)
List<User> users = getUserList();
List<User> result2 = users.stream()
    .peek(user -> user.setProcessed(true))  // 修改对象状态
    .collect(Collectors.toList());

// 3. 记录日志
List<String> names = users.stream()
    .map(User::getName)
    .peek(name -> log.info("处理用户: {}", name))
    .collect(Collectors.toList());

// 4. 统计信息
AtomicInteger counter = new AtomicInteger(0);
List<Integer> result3 = numbers.stream()
    .peek(n -> counter.incrementAndGet())
    .filter(n -> n > 5)
    .collect(Collectors.toList());
System.out.println("处理了 " + counter.get() + " 个元素");

// 5. 性能监控
List<String> result4 = words.stream()
    .peek(word -> {
        long start = System.nanoTime();
        // 处理逻辑
        long end = System.nanoTime();
        System.out.println(word + " 处理耗时: " + (end - start) + "ns");
    })
    .collect(Collectors.toList());

peek vs forEach:

peek:
- 中间操作,返回Stream
- 用于调试和查看中间结果
- 不会触发流的执行

forEach:
- 终止操作,无返回值
- 用于最终消费数据
- 会触发流的执行

五、Stream终止操作详解

5.1 collect - 收集结果

// 语法
<R, A> R collect(Collector<? super T, A, R> collector)

// 作用:将流转换为其他数据结构

常用Collectors:

// 1. 转换为List
List<String> list = stream.collect(Collectors.toList());

// 2. 转换为Set
Set<String> set = stream.collect(Collectors.toSet());

// 3. 转换为Map
Map<Long, User> map = users.stream()
    .collect(Collectors.toMap(
        User::getId,        // key
        user -> user        // value
    ));

// 处理key重复
Map<String, User> map2 = users.stream()
    .collect(Collectors.toMap(
        User::getName,
        user -> user,
        (existing, replacement) -> existing  // key重复时保留第一个
    ));

// 4. 转换为指定集合类型
ArrayList<String> arrayList = stream.collect(Collectors.toCollection(ArrayList::new));
LinkedList<String> linkedList = stream.collect(Collectors.toCollection(LinkedList::new));
TreeSet<String> treeSet = stream.collect(Collectors.toCollection(TreeSet::new));

// 5. 拼接字符串
String joined = Stream.of("A", "B", "C")
    .collect(Collectors.joining());
// 结果:"ABC"

String joinedWithDelimiter = Stream.of("A", "B", "C")
    .collect(Collectors.joining(", "));
// 结果:"A, B, C"

String joinedWithPrefixSuffix = Stream.of("A", "B", "C")
    .collect(Collectors.joining(", ", "[", "]"));
// 结果:"[A, B, C]"

// 6. 分组
Map<String, List<User>> groupByGender = users.stream()
    .collect(Collectors.groupingBy(User::getGender));
// 结果:{"男": [Bob, Charlie], "女": [Alice, Diana]}

// 多级分组
Map<String, Map<Integer, List<User>>> multiGroup = users.stream()
    .collect(Collectors.groupingBy(
        User::getGender,
        Collectors.groupingBy(user -> user.getAge() / 10)
    ));
// 结果:{"男": {2: [Charlie], 3: [Bob]}, "女": {2: [Alice, Diana]}}

// 7. 分区(特殊的分组,只有true/false两个key)
Map<Boolean, List<User>> partition = users.stream()
    .collect(Collectors.partitioningBy(user -> user.getAge() >= 25));
// 结果:{true: [Alice, Bob, Diana], false: [Charlie]}

// 8. 统计
Long count = users.stream()
    .collect(Collectors.counting());

Integer sum = numbers.stream()
    .collect(Collectors.summingInt(Integer::intValue));

Double average = numbers.stream()
    .collect(Collectors.averagingInt(Integer::intValue));

Optional<Integer> max = numbers.stream()
    .collect(Collectors.maxBy(Integer::compareTo));

Optional<Integer> min = numbers.stream()
    .collect(Collectors.minBy(Integer::compareTo));

// 综合统计
IntSummaryStatistics stats = numbers.stream()
    .collect(Collectors.summarizingInt(Integer::intValue));
System.out.println("Count: " + stats.getCount());
System.out.println("Sum: " + stats.getSum());
System.out.println("Min: " + stats.getMin());
System.out.println("Max: " + stats.getMax());
System.out.println("Average: " + stats.getAverage());

// 9. 下游收集器
// 分组后统计每组数量
Map<String, Long> countByGender = users.stream()
    .collect(Collectors.groupingBy(
        User::getGender,
        Collectors.counting()
    ));
// 结果:{"男": 2, "女": 2}

// 分组后获取每组的名字列表
Map<String, List<String>> namesByGender = users.stream()
    .collect(Collectors.groupingBy(
        User::getGender,
        Collectors.mapping(User::getName, Collectors.toList())
    ));
// 结果:{"男": ["Bob", "Charlie"], "女": ["Alice", "Diana"]}

// 分组后计算每组平均年龄
Map<String, Double> avgAgeByGender = users.stream()
    .collect(Collectors.groupingBy(
        User::getGender,
        Collectors.averagingInt(User::getAge)
    ));

// 10. 自定义Collector
Collector<User, ?, String> customCollector = Collector.of(
    StringBuilder::new,                          // supplier
    (sb, user) -> sb.append(user.getName()).append(","),  // accumulator
    StringBuilder::append,                       // combiner
    StringBuilder::toString                      // finisher
);

String result = users.stream().collect(customCollector);

5.2 reduce - 归约

// 语法
Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)

// 作用:将流中的元素组合起来

实际示例:

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

// 1. 求和
Optional<Integer> sum1 = numbers.stream()
    .reduce((a, b) -> a + b);
// 结果:Optional[15]

Integer sum2 = numbers.stream()
    .reduce(0, (a, b) -> a + b);
// 结果:15

// 使用方法引用
Integer sum3 = numbers.stream()
    .reduce(0, Integer::sum);

// 2. 求最大值
Optional<Integer> max = numbers.stream()
    .reduce((a, b) -> a > b ? a : b);

Optional<Integer> max2 = numbers.stream()
    .reduce(Integer::max);

// 3. 求最小值
Optional<Integer> min = numbers.stream()
    .reduce(Integer::min);

// 4. 字符串拼接
List<String> words = Arrays.asList("Hello", "World", "Java");
String joined = words.stream()
    .reduce("", (a, b) -> a + " " + b);
// 结果:" Hello World Java"

String joined2 = words.stream()
    .reduce("", (a, b) -> a.isEmpty() ? b : a + " " + b);
// 结果:"Hello World Java"

// 5. 复杂对象归约
List<User> users = getUserList();

// 计算总年龄
Integer totalAge = users.stream()
    .map(User::getAge)
    .reduce(0, Integer::sum);

// 找出年龄最大的用户
Optional<User> oldest = users.stream()
    .reduce((u1, u2) -> u1.getAge() > u2.getAge() ? u1 : u2);

// 6. 并行流的reduce
Integer parallelSum = numbers.parallelStream()
    .reduce(0,                    // identity
            Integer::sum,         // accumulator
            Integer::sum);        // combiner(并行时合并结果)

// 7. 实际业务场景
class Order {
    private BigDecimal amount;
    // getter/setter
}

List<Order> orders = getOrders();

// 计算订单总金额
BigDecimal totalAmount = orders.stream()
    .map(Order::getAmount)
    .reduce(BigDecimal.ZERO, BigDecimal::add);

// 8. reduce vs collect
// reduce:将多个元素合并为一个
// collect:将多个元素收集到容器中

// 使用reduce(不推荐)
List<Integer> list1 = numbers.stream()
    .reduce(new ArrayList<>(),
            (list, item) -> {
                list.add(item);
                return list;
            },
            (list1, list2) -> {
                list1.addAll(list2);
                return list1;
            });

// 使用collect(推荐)
List<Integer> list2 = numbers.stream()
    .collect(Collectors.toList());

reduce工作原理:

numbers = [1, 2, 3, 4, 5]
reduce(0, Integer::sum)

执行过程:
0 + 1 = 1
1 + 2 = 3
3 + 3 = 6
6 + 4 = 10
10 + 5 = 15

可视化:
    0
    │
    ├─ 1 → 1
    │
    ├─ 2 → 3
    │
    ├─ 3 → 6
    │
    ├─ 4 → 10
    │
    └─ 5 → 15

5.3 forEach - 遍历

// 语法
void forEach(Consumer<? super T> action)

// 作用:对每个元素执行操作

实际示例:

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

// 1. 简单遍历
names.stream()
    .forEach(name -> System.out.println(name));

// 使用方法引用
names.stream()
    .forEach(System.out::println);

// 2. 带索引遍历
AtomicInteger index = new AtomicInteger(0);
names.stream()
    .forEach(name -> {
        System.out.println(index.getAndIncrement() + ": " + name);
    });

// 3. 修改对象(副作用操作)
List<User> users = getUserList();
users.stream()
    .forEach(user -> user.setProcessed(true));

// 4. 批量操作
List<Long> userIds = Arrays.asList(1L, 2L, 3L);
userIds.stream()
    .forEach(id -> userService.deleteById(id));

// 5. forEach vs forEachOrdered
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// forEach:并行流中顺序不保证
numbers.parallelStream()
    .forEach(System.out::println);
// 可能输出:3, 1, 4, 2, 5(顺序不确定)

// forEachOrdered:保证顺序
numbers.parallelStream()
    .forEachOrdered(System.out::println);
// 输出:1, 2, 3, 4, 5(顺序确定)

// 6. 实际业务场景
// 发送邮件
users.stream()
    .filter(user -> user.isActive())
    .forEach(user -> emailService.send(user.getEmail(), "通知内容"));

// 更新数据库
orders.stream()
    .filter(order -> order.getStatus() == OrderStatus.PENDING)
    .forEach(order -> {
        order.setStatus(OrderStatus.PROCESSING);
        orderRepository.save(order);
    });

5.4 count、min、max - 统计

// 语法
long count()
Optional<T> min(Comparator<? super T> comparator)
Optional<T> max(Comparator<? super T> comparator)

// 作用:统计流中的元素

实际示例:

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

// 1. count - 计数
long count = numbers.stream().count();
// 结果:5

long evenCount = numbers.stream()
    .filter(n -> n % 2 == 0)
    .count();
// 结果:2

// 2. min - 最小值
Optional<Integer> min = numbers.stream()
    .min(Integer::compareTo);
// 结果:Optional[1]

// 3. max - 最大值
Optional<Integer> max = numbers.stream()
    .max(Integer::compareTo);
// 结果:Optional[5]

// 4. 对象的min/max
List<User> users = getUserList();

// 年龄最小的用户
Optional<User> youngest = users.stream()
    .min(Comparator.comparing(User::getAge));

// 年龄最大的用户
Optional<User> oldest = users.stream()
    .max(Comparator.comparing(User::getAge));

// 名字字典序最小的用户
Optional<User> firstByName = users.stream()
    .min(Comparator.comparing(User::getName));

// 5. 实际业务场景
class Order {
    private BigDecimal amount;
    private LocalDateTime createTime;
    // getter/setter
}

List<Order> orders = getOrders();

// 订单总数
long totalOrders = orders.stream().count();

// 最大订单金额
Optional<BigDecimal> maxAmount = orders.stream()
    .map(Order::getAmount)
    .max(BigDecimal::compareTo);

// 最早的订单
Optional<Order> earliestOrder = orders.stream()
    .min(Comparator.comparing(Order::getCreateTime));

// 最新的订单
Optional<Order> latestOrder = orders.stream()
    .max(Comparator.comparing(Order::getCreateTime));

// 6. 处理Optional结果
Optional<Integer> max2 = numbers.stream().max(Integer::compareTo);

// 方式1:isPresent + get
if (max2.isPresent()) {
    System.out.println("最大值: " + max2.get());
}

// 方式2:orElse
Integer maxValue = max2.orElse(0);

// 方式3:orElseGet
Integer maxValue2 = max2.orElseGet(() -> getDefaultValue());

// 方式4:orElseThrow
Integer maxValue3 = max2.orElseThrow(() -> new RuntimeException("没有数据"));

// 方式5:ifPresent
max2.ifPresent(value -> System.out.println("最大值: " + value));

5.5 anyMatch、allMatch、noneMatch - 匹配

// 语法
boolean anyMatch(Predicate<? super T> predicate)   // 任意一个匹配
boolean allMatch(Predicate<? super T> predicate)   // 全部匹配
boolean noneMatch(Predicate<? super T> predicate)  // 全部不匹配

// 作用:判断流中的元素是否满足条件

实际示例:

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

// 1. anyMatch - 是否存在偶数
boolean hasEven = numbers.stream()
    .anyMatch(n -> n % 2 == 0);
// 结果:true

// 2. allMatch - 是否全部为正数
boolean allPositive = numbers.stream()
    .allMatch(n -> n > 0);
// 结果:true

// 3. noneMatch - 是否没有负数
boolean noNegative = numbers.stream()
    .noneMatch(n -> n < 0);
// 结果:true

// 4. 实际业务场景
List<User> users = getUserList();

// 是否存在成年用户
boolean hasAdult = users.stream()
    .anyMatch(user -> user.getAge() >= 18);

// 是否全部是成年用户
boolean allAdult = users.stream()
    .allMatch(user -> user.getAge() >= 18);

// 是否没有未成年用户
boolean noMinor = users.stream()
    .noneMatch(user -> user.getAge() < 18);

// 5. 短路操作
// anyMatch找到第一个匹配就停止
boolean result = Stream.of(1, 2, 3, 4, 5)
    .peek(n -> System.out.println("检查: " + n))
    .anyMatch(n -> n > 3);
// 输出:
// 检查: 1
// 检查: 2
// 检查: 3
// 检查: 4
// 结果:true(找到4后就停止了)

// 6. 复杂条件判断
class Order {
    private OrderStatus status;
    private BigDecimal amount;
    // getter/setter
}

List<Order> orders = getOrders();

// 是否存在待支付订单
boolean hasPendingOrder = orders.stream()
    .anyMatch(order -> order.getStatus() == OrderStatus.PENDING);

// 是否所有订单都已完成
boolean allCompleted = orders.stream()
    .allMatch(order -> order.getStatus() == OrderStatus.COMPLETED);

// 是否没有超过1000元的订单
boolean noLargeOrder = orders.stream()
    .noneMatch(order -> order.getAmount().compareTo(new BigDecimal("1000")) > 0);

// 7. 组合使用
// 是否存在年龄在18-30之间的女性用户
boolean exists = users.stream()
    .anyMatch(user -> 
        user.getAge() >= 18 && 
        user.getAge() <= 30 && 
        "女".equals(user.getGender())
    );

// 8. 空流处理
boolean result1 = Stream.empty().anyMatch(x -> true);   // false
boolean result2 = Stream.empty().allMatch(x -> false);  // true(空集合的全称命题为真)
boolean result3 = Stream.empty().noneMatch(x -> true);  // true

5.6 findFirst、findAny - 查找

// 语法
Optional<T> findFirst()  // 返回第一个元素
Optional<T> findAny()    // 返回任意一个元素

// 作用:查找流中的元素

实际示例:

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

// 1. findFirst - 查找第一个
Optional<Integer> first = numbers.stream()
    .findFirst();
// 结果:Optional[1]

// 2. findAny - 查找任意一个
Optional<Integer> any = numbers.stream()
    .findAny();
// 结果:Optional[1](串行流中通常返回第一个)

// 3. 并行流中的区别
Optional<Integer> parallelFirst = numbers.parallelStream()
    .findFirst();
// 结果:Optional[1](保证返回第一个)

Optional<Integer> parallelAny = numbers.parallelStream()
    .findAny();
// 结果:Optional[?](可能返回任意一个,性能更好)

// 4. 结合filter使用
List<User> users = getUserList();

// 查找第一个成年用户
Optional<User> firstAdult = users.stream()
    .filter(user -> user.getAge() >= 18)
    .findFirst();

// 查找任意一个女性用户
Optional<User> anyFemale = users.stream()
    .filter(user -> "女".equals(user.getGender()))
    .findAny();

// 5. 处理查找结果
Optional<User> user = users.stream()
    .filter(u -> u.getId().equals(userId))
    .findFirst();

// 方式1:isPresent + get
if (user.isPresent()) {
    User u = user.get();
    System.out.println(u.getName());
}

// 方式2:orElse
User u = user.orElse(new User("默认用户"));

// 方式3:orElseThrow
User u2 = user.orElseThrow(() -> new UserNotFoundException("用户不存在"));

// 方式4:ifPresent
user.ifPresent(u -> System.out.println(u.getName()));

// 方式5:map
String name = user.map(User::getName).orElse("未知");

// 6. 实际业务场景
class Product {
    private String name;
    private BigDecimal price;
    private boolean inStock;
    // getter/setter
}

List<Product> products = getProducts();

// 查找第一个有货的商品
Optional<Product> availableProduct = products.stream()
    .filter(Product::isInStock)
    .findFirst();

// 查找价格最低的商品
Optional<Product> cheapest = products.stream()
    .min(Comparator.comparing(Product::getPrice));

// 查找特定名称的商品
Optional<Product> product = products.stream()
    .filter(p -> "iPhone".equals(p.getName()))
    .findFirst();

// 7. 短路操作
Optional<Integer> result = Stream.of(1, 2, 3, 4, 5)
    .peek(n -> System.out.println("处理: " + n))
    .filter(n -> n > 3)
    .findFirst();
// 输出:
// 处理: 1
// 处理: 2
// 处理: 3
// 处理: 4
// 结果:Optional[4](找到后就停止了)

// 8. findFirst vs findAny性能对比
// 串行流:性能相同
// 并行流:findAny性能更好(不需要保证顺序)

// 推荐:
// - 需要确定顺序时使用findFirst
// - 不关心顺序且使用并行流时使用findAny

六、数值流(IntStream、LongStream、DoubleStream)

6.1 为什么需要数值流?

// 问题:装箱拆箱的性能开销
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 使用Stream<Integer>
int sum1 = numbers.stream()
    .reduce(0, Integer::sum);
// 问题:每次操作都需要装箱/拆箱

// 使用IntStream
int sum2 = numbers.stream()
    .mapToInt(Integer::intValue)
    .sum();
// 优势:避免装箱拆箱,性能更好

数值流的优势:

  • 避免装箱拆箱开销
  • 提供专门的数值操作方法(sum、average、max、min)
  • 支持范围生成(range、rangeClosed)

6.2 创建数值流

// 1. 从集合转换
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
IntStream intStream = numbers.stream()
    .mapToInt(Integer::intValue);

// 2. 直接创建
IntStream intStream1 = IntStream.of(1, 2, 3, 4, 5);
LongStream longStream = LongStream.of(1L, 2L, 3L);
DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0);

// 3. 范围生成
IntStream range1 = IntStream.range(1, 10);        // 1到9
IntStream range2 = IntStream.rangeClosed(1, 10);  // 1到10

LongStream longRange = LongStream.range(1L, 100L);

// 4. 从数组创建
int[] array = {1, 2, 3, 4, 5};
IntStream intStream2 = Arrays.stream(array);

// 5. 无限流
IntStream infiniteStream = IntStream.iterate(0, n -> n + 2);
IntStream randomStream = IntStream.generate(() -> (int)(Math.random() * 100));

6.3 数值流操作

IntStream numbers = IntStream.of(1, 2, 3, 4, 5);

// 1. 统计操作
int sum = numbers.sum();                    // 求和:15
OptionalDouble avg = numbers.average();     // 平均值:3.0
OptionalInt max = numbers.max();            // 最大值:5
OptionalInt min = numbers.min();            // 最小值:1
long count = numbers.count();               // 计数:5

// 2. 综合统计
IntSummaryStatistics stats = IntStream.of(1, 2, 3, 4, 5)
    .summaryStatistics();

System.out.println("Count: " + stats.getCount());      // 5
System.out.println("Sum: " + stats.getSum());          // 15
System.out.println("Min: " + stats.getMin());          // 1
System.out.println("Max: " + stats.getMax());          // 5
System.out.println("Average: " + stats.getAverage());  // 3.0

// 3. 转换为对象流
Stream<Integer> boxed = IntStream.of(1, 2, 3)
    .boxed();

Stream<String> strings = IntStream.of(1, 2, 3)
    .mapToObj(n -> "Number: " + n);

// 4. 类型转换
IntStream intStream = IntStream.of(1, 2, 3);
LongStream longStream = intStream.asLongStream();
DoubleStream doubleStream = intStream.asDoubleStream();

// 5. 实际应用
// 计算1到100的和
int sum1 = IntStream.rangeClosed(1, 100).sum();

// 计算1到100的偶数和
int evenSum = IntStream.rangeClosed(1, 100)
    .filter(n -> n % 2 == 0)
    .sum();

// 生成随机数
List<Integer> randomNumbers = IntStream.generate(() -> (int)(Math.random() * 100))
    .limit(10)
    .boxed()
    .collect(Collectors.toList());

// 6. 业务场景
List<Order> orders = getOrders();

// 计算订单总金额(使用DoubleStream)
double totalAmount = orders.stream()
    .mapToDouble(order -> order.getAmount().doubleValue())
    .sum();

// 计算平均订单金额
OptionalDouble avgAmount = orders.stream()
    .mapToDouble(order -> order.getAmount().doubleValue())
    .average();

// 统计用户年龄
IntSummaryStatistics ageStats = users.stream()
    .mapToInt(User::getAge)
    .summaryStatistics();

System.out.println("平均年龄: " + ageStats.getAverage());
System.out.println("最大年龄: " + ageStats.getMax());
System.out.println("最小年龄: " + ageStats.getMin());

七、并行流

7.1 什么是并行流?

串行流(Sequential Stream):
数据 → 操作1 → 操作2 → 操作3 → 结果
      单线程顺序执行

并行流(Parallel Stream):
数据 → 分割 → 多线程并行处理 → 合并 → 结果
      ├─ 线程1 → 操作
      ├─ 线程2 → 操作
      ├─ 线程3 → 操作
      └─ 线程4 → 操作

7.2 创建并行流

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

// 方式1:从集合创建
Stream<Integer> parallelStream = numbers.parallelStream();

// 方式2:将串行流转为并行流
Stream<Integer> parallelStream2 = numbers.stream().parallel();

// 方式3:将并行流转为串行流
Stream<Integer> sequentialStream = parallelStream.sequential();

7.3 并行流示例

List<Integer> numbers = IntStream.rangeClosed(1, 1000000)
    .boxed()
    .collect(Collectors.toList());

// 串行流
long start1 = System.currentTimeMillis();
long sum1 = numbers.stream()
    .mapToLong(Integer::longValue)
    .sum();
long end1 = System.currentTimeMillis();
System.out.println("串行流耗时: " + (end1 - start1) + "ms");

// 并行流
long start2 = System.currentTimeMillis();
long sum2 = numbers.parallelStream()
    .mapToLong(Integer::longValue)
    .sum();
long end2 = System.currentTimeMillis();
System.out.println("并行流耗时: " + (end2 - start2) + "ms");

7.4 并行流注意事项

// 1. 线程安全问题
List<Integer> numbers = IntStream.rangeClosed(1, 1000).boxed().collect(Collectors.toList());

// ❌ 错误:ArrayList不是线程安全的
List<Integer> result1 = new ArrayList<>();
numbers.parallelStream()
    .forEach(n -> result1.add(n * 2));
// 结果:可能丢失数据或抛出异常

// ✓ 正确:使用collect
List<Integer> result2 = numbers.parallelStream()
    .map(n -> n * 2)
    .collect(Collectors.toList());

// ✓ 正确:使用线程安全的集合
List<Integer> result3 = new CopyOnWriteArrayList<>();
numbers.parallelStream()
    .forEach(n -> result3.add(n * 2));

// 2. 避免有状态的Lambda
// ❌ 错误:依赖外部可变状态
int[] sum = {0};
numbers.parallelStream()
    .forEach(n -> sum[0] += n);  // 线程不安全

// ✓ 正确:使用reduce
int sum2 = numbers.parallelStream()
    .reduce(0, Integer::sum);

// 3. 避免阻塞操作
// ❌ 不推荐:并行流中执行阻塞操作
numbers.parallelStream()
    .forEach(n -> {
        try {
            Thread.sleep(100);  // 阻塞操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });

// 4. 数据量小时不要使用并行流
List<Integer> smallList = Arrays.asList(1, 2, 3, 4, 5);

// 数据量小,串行流更快(避免线程切换开销)
int sum3 = smallList.stream().mapToInt(Integer::intValue).sum();

// 5. 适合并行的场景
// ✓ 数据量大
// ✓ 计算密集型操作
// ✓ 无状态操作
// ✓ 无依赖关系

List<String> largeList = getLargeList();  // 百万级数据

// 适合并行:计算密集型
List<String> result = largeList.parallelStream()
    .map(s -> complexCalculation(s))
    .collect(Collectors.toList());

// 不适合并行:IO密集型
List<String> result2 = largeList.parallelStream()
    .map(s -> callRemoteApi(s))  // 网络IO
    .collect(Collectors.toList());

7.5 并行流性能优化

// 1. 使用数值流避免装箱拆箱
// ❌ 性能较差
long sum1 = numbers.parallelStream()
    .reduce(0, Integer::sum);

// ✓ 性能更好
long sum2 = numbers.parallelStream()
    .mapToInt(Integer::intValue)
    .sum();

// 2. 合理设置并行度
// 默认并行度 = CPU核心数
int parallelism = ForkJoinPool.commonPool().getParallelism();
System.out.println("默认并行度: " + parallelism);

// 自定义ForkJoinPool
ForkJoinPool customPool = new ForkJoinPool(4);
try {
    long sum = customPool.submit(() ->
        numbers.parallelStream()
            .mapToInt(Integer::intValue)
            .sum()
    ).get();
} catch (Exception e) {
    e.printStackTrace();
}

// 3. 选择合适的数据结构
// ArrayList:分割效率高,适合并行
// LinkedList:分割效率低,不适合并行
// HashSet:分割效率中等
// TreeSet:分割效率中等

List<Integer> arrayList = new ArrayList<>(numbers);
List<Integer> linkedList = new LinkedList<>(numbers);

// ArrayList并行效率高
long sum3 = arrayList.parallelStream()
    .mapToInt(Integer::intValue)
    .sum();

// LinkedList并行效率低
long sum4 = linkedList.parallelStream()
    .mapToInt(Integer::intValue)
    .sum();

八、实战案例

8.1 数据统计分析

/**
 * 场景:分析用户数据
 */
class User {
    private Long id;
    private String name;
    private Integer age;
    private String gender;
    private String city;
    private BigDecimal salary;
    // getter/setter
}

List<User> users = getUserList();

// 1. 统计男女用户数量
Map<String, Long> genderCount = users.stream()
    .collect(Collectors.groupingBy(
        User::getGender,
        Collectors.counting()
    ));
System.out.println("男性用户: " + genderCount.get("男"));
System.out.println("女性用户: " + genderCount.get("女"));

// 2. 计算各城市平均工资
Map<String, Double> avgSalaryByCity = users.stream()
    .collect(Collectors.groupingBy(
        User::getCity,
        Collectors.averagingDouble(u -> u.getSalary().doubleValue())
    ));

avgSalaryByCity.forEach((city, avgSalary) -> 
    System.out.println(city + "平均工资: " + avgSalary)
);

// 3. 找出工资最高的前10名用户
List<User> top10 = users.stream()
    .sorted(Comparator.comparing(User::getSalary).reversed())
    .limit(10)
    .collect(Collectors.toList());

// 4. 按年龄段分组统计
Map<String, Long> ageGroupCount = users.stream()
    .collect(Collectors.groupingBy(
        user -> {
            int age = user.getAge();
            if (age < 20) return "20岁以下";
            else if (age < 30) return "20-30岁";
            else if (age < 40) return "30-40岁";
            else return "40岁以上";
        },
        Collectors.counting()
    ));

// 5. 统计各城市男女用户数量
Map<String, Map<String, Long>> cityGenderCount = users.stream()
    .collect(Collectors.groupingBy(
        User::getCity,
        Collectors.groupingBy(
            User::getGender,
            Collectors.counting()
        )
    ));

// 6. 计算工资总和、平均值、最大值、最小值
DoubleSummaryStatistics salaryStats = users.stream()
    .mapToDouble(u -> u.getSalary().doubleValue())
    .summaryStatistics();

System.out.println("工资总和: " + salaryStats.getSum());
System.out.println("平均工资: " + salaryStats.getAverage());
System.out.println("最高工资: " + salaryStats.getMax());
System.out.println("最低工资: " + salaryStats.getMin());

// 7. 找出每个城市工资最高的用户
Map<String, Optional<User>> highestSalaryByCity = users.stream()
    .collect(Collectors.groupingBy(
        User::getCity,
        Collectors.maxBy(Comparator.comparing(User::getSalary))
    ));

// 8. 生成用户报表
String report = users.stream()
    .map(user -> String.format("%s,%d,%s,%s,%.2f",
        user.getName(),
        user.getAge(),
        user.getGender(),
        user.getCity(),
        user.getSalary()
    ))
    .collect(Collectors.joining("\n", "姓名,年龄,性别,城市,工资\n", ""));

System.out.println(report);

8.2 订单数据处理

/**
 * 场景:电商订单分析
 */
class Order {
    private Long id;
    private Long userId;
    private BigDecimal amount;
    private OrderStatus status;
    private LocalDateTime createTime;
    private List<OrderItem> items;
    // getter/setter
}

class OrderItem {
    private Long productId;
    private String productName;
    private Integer quantity;
    private BigDecimal price;
    // getter/setter
}

List<Order> orders = getOrders();

// 1. 计算总销售额
BigDecimal totalSales = orders.stream()
    .filter(order -> order.getStatus() == OrderStatus.COMPLETED)
    .map(Order::getAmount)
    .reduce(BigDecimal.ZERO, BigDecimal::add);

// 2. 统计各状态订单数量
Map<OrderStatus, Long> statusCount = orders.stream()
    .collect(Collectors.groupingBy(
        Order::getStatus,
        Collectors.counting()
    ));

// 3. 找出消费最多的前10名用户
Map<Long, BigDecimal> userTotalAmount = orders.stream()
    .filter(order -> order.getStatus() == OrderStatus.COMPLETED)
    .collect(Collectors.groupingBy(
        Order::getUserId,
        Collectors.reducing(
            BigDecimal.ZERO,
            Order::getAmount,
            BigDecimal::add
        )
    ));

List<Map.Entry<Long, BigDecimal>> top10Users = userTotalAmount.entrySet().stream()
    .sorted(Map.Entry.<Long, BigDecimal>comparingByValue().reversed())
    .limit(10)
    .collect(Collectors.toList());

// 4. 统计每天的订单数量
Map<LocalDate, Long> dailyOrderCount = orders.stream()
    .collect(Collectors.groupingBy(
        order -> order.getCreateTime().toLocalDate(),
        Collectors.counting()
    ));

// 5. 统计每天的销售额
Map<LocalDate, BigDecimal> dailySales = orders.stream()
    .filter(order -> order.getStatus() == OrderStatus.COMPLETED)
    .collect(Collectors.groupingBy(
        order -> order.getCreateTime().toLocalDate(),
        Collectors.reducing(
            BigDecimal.ZERO,
            Order::getAmount,
            BigDecimal::add
        )
    ));

// 6. 找出最畅销的商品
Map<Long, Long> productSales = orders.stream()
    .filter(order -> order.getStatus() == OrderStatus.COMPLETED)
    .flatMap(order -> order.getItems().stream())
    .collect(Collectors.groupingBy(
        OrderItem::getProductId,
        Collectors.summingLong(OrderItem::getQuantity)
    ));

Optional<Map.Entry<Long, Long>> bestSeller = productSales.entrySet().stream()
    .max(Map.Entry.comparingByValue());

// 7. 计算平均订单金额
OptionalDouble avgOrderAmount = orders.stream()
    .filter(order -> order.getStatus() == OrderStatus.COMPLETED)
    .mapToDouble(order -> order.getAmount().doubleValue())
    .average();

// 8. 找出超过1000元的大额订单
List<Order> largeOrders = orders.stream()
    .filter(order -> order.getAmount().compareTo(new BigDecimal("1000")) > 0)
    .sorted(Comparator.comparing(Order::getAmount).reversed())
    .collect(Collectors.toList());

// 9. 统计各时间段订单数量
Map<String, Long> hourlyOrderCount = orders.stream()
    .collect(Collectors.groupingBy(
        order -> {
            int hour = order.getCreateTime().getHour();
            if (hour < 6) return "凌晨(0-6点)";
            else if (hour < 12) return "上午(6-12点)";
            else if (hour < 18) return "下午(12-18点)";
            else return "晚上(18-24点)";
        },
        Collectors.counting()
    ));

// 10. 生成订单报表
String orderReport = orders.stream()
    .sorted(Comparator.comparing(Order::getCreateTime).reversed())
    .limit(100)
    .map(order -> String.format(
        "订单号: %d, 用户ID: %d, 金额: %.2f, 状态: %s, 时间: %s",
        order.getId(),
        order.getUserId(),
        order.getAmount(),
        order.getStatus(),
        order.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
    ))
    .collect(Collectors.joining("\n"));

8.3 文本数据处理

/**
 * 场景:日志文件分析
 */
public class LogAnalyzer {
    
    public static void analyzeLog(String logFilePath) throws IOException {
        try (Stream<String> lines = Files.lines(Paths.get(logFilePath))) {
            
            // 1. 统计各级别日志数量
            Map<String, Long> levelCount = lines
                .filter(line -> line.contains("ERROR") || 
                               line.contains("WARN") || 
                               line.contains("INFO"))
                .collect(Collectors.groupingBy(
                    line -> {
                        if (line.contains("ERROR")) return "ERROR";
                        else if (line.contains("WARN")) return "WARN";
                        else return "INFO";
                    },
                    Collectors.counting()
                ));
            
            System.out.println("日志级别统计: " + levelCount);
        }
        
        // 2. 提取所有ERROR日志
        try (Stream<String> lines = Files.lines(Paths.get(logFilePath))) {
            List<String> errors = lines
                .filter(line -> line.contains("ERROR"))
                .collect(Collectors.toList());
            
            System.out.println("ERROR日志数量: " + errors.size());
        }
        
        // 3. 统计最常见的错误信息
        try (Stream<String> lines = Files.lines(Paths.get(logFilePath))) {
            Map<String, Long> errorCount = lines
                .filter(line -> line.contains("ERROR"))
                .map(line -> extractErrorMessage(line))
                .collect(Collectors.groupingBy(
                    Function.identity(),
                    Collectors.counting()
                ));
            
            // 找出出现次数最多的错误
            errorCount.entrySet().stream()
                .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
                .limit(10)
                .forEach(entry -> 
                    System.out.println(entry.getKey() + ": " + entry.getValue() + "次")
                );
        }
        
        // 4. 按时间段统计日志
        try (Stream<String> lines = Files.lines(Paths.get(logFilePath))) {
            Map<String, Long> hourlyCount = lines
                .map(line -> extractHour(line))
                .filter(Objects::nonNull)
                .collect(Collectors.groupingBy(
                    Function.identity(),
                    Collectors.counting()
                ));
            
            System.out.println("各时段日志数量: " + hourlyCount);
        }
    }
    
    private static String extractErrorMessage(String line) {
        // 提取错误信息的逻辑
        return line.substring(line.indexOf("ERROR") + 6);
    }
    
    private static String extractHour(String line) {
        // 提取小时的逻辑
        Pattern pattern = Pattern.compile("\\d{2}:\\d{2}:\\d{2}");
        Matcher matcher = pattern.matcher(line);
        if (matcher.find()) {
            return matcher.group().substring(0, 2);
        }
        return null;
    }
}

/**
 * 场景:文本词频统计
 */
public class WordFrequency {
    
    public static Map<String, Long> countWords(String text) {
        return Arrays.stream(text.split("\\s+"))
            .map(String::toLowerCase)
            .filter(word -> !word.isEmpty())
            .collect(Collectors.groupingBy(
                Function.identity(),
                Collectors.counting()
            ));
    }
    
    public static List<Map.Entry<String, Long>> getTopWords(String text, int n) {
        return countWords(text).entrySet().stream()
            .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
            .limit(n)
            .collect(Collectors.toList());
    }
    
    public static void main(String[] args) throws IOException {
        // 读取文件内容
        String content = Files.lines(Paths.get("article.txt"))
            .collect(Collectors.joining(" "));
        
        // 统计词频
        Map<String, Long> wordCount = countWords(content);
        System.out.println("总词数: " + wordCount.size());
        
        // 获取出现最多的10个词
        List<Map.Entry<String, Long>> top10 = getTopWords(content, 10);
        System.out.println("Top 10词汇:");
        top10.forEach(entry -> 
            System.out.println(entry.getKey() + ": " + entry.getValue())
        );
    }
}

8.4 数据转换与映射

/**
 * 场景:DTO转换
 */
class UserEntity {
    private Long id;
    private String username;
    private String password;
    private String email;
    private LocalDateTime createTime;
    // getter/setter
}

class UserDTO {
    private Long id;
    private String username;
    private String email;
    // getter/setter
    
    public static UserDTO fromEntity(UserEntity entity) {
        UserDTO dto = new UserDTO();
        dto.setId(entity.getId());
        dto.setUsername(entity.getUsername());
        dto.setEmail(entity.getEmail());
        return dto;
    }
}

// 批量转换
List<UserEntity> entities = userRepository.findAll();
List<UserDTO> dtos = entities.stream()
    .map(UserDTO::fromEntity)
    .collect(Collectors.toList());

/**
 * 场景:数据清洗
 */
class RawData {
    private String name;
    private String age;
    private String email;
    // getter/setter
}

class CleanData {
    private String name;
    private Integer age;
    private String email;
    // getter/setter
}

List<RawData> rawDataList = getRawData();

List<CleanData> cleanDataList = rawDataList.stream()
    // 过滤无效数据
    .filter(data -> data.getName() != null && !data.getName().isEmpty())
    .filter(data -> data.getAge() != null && data.getAge().matches("\\d+"))
    .filter(data -> data.getEmail() != null && data.getEmail().contains("@"))
    // 转换数据
    .map(raw -> {
        CleanData clean = new CleanData();
        clean.setName(raw.getName().trim());
        clean.setAge(Integer.parseInt(raw.getAge()));
        clean.setEmail(raw.getEmail().toLowerCase());
        return clean;
    })
    // 去重
    .distinct()
    .collect(Collectors.toList());

/**
 * 场景:树形结构转换
 */
class Department {
    private Long id;
    private String name;
    private Long parentId;
    private List<Department> children;
    // getter/setter
}

public static List<Department> buildTree(List<Department> allDepts) {
    // 创建id到部门的映射
    Map<Long, Department> deptMap = allDepts.stream()
        .collect(Collectors.toMap(Department::getId, dept -> dept));
    
    // 构建树形结构
    return allDepts.stream()
        .filter(dept -> dept.getParentId() == null || dept.getParentId() == 0)
        .peek(dept -> buildChildren(dept, deptMap))
        .collect(Collectors.toList());
}

private static void buildChildren(Department parent, Map<Long, Department> deptMap) {
    List<Department> children = deptMap.values().stream()
        .filter(dept -> parent.getId().equals(dept.getParentId()))
        .peek(child -> buildChildren(child, deptMap))
        .collect(Collectors.toList());
    parent.setChildren(children);
}

九、性能优化技巧

9.1 避免不必要的操作

List<User> users = getUserList();

// ❌ 不好:多次遍历
List<String> names = users.stream()
    .map(User::getName)
    .collect(Collectors.toList());

List<String> upperNames = names.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());

// ✓ 好:一次遍历
List<String> upperNames2 = users.stream()
    .map(User::getName)
    .map(String::toUpperCase)
    .collect(Collectors.toList());

9.2 使用短路操作

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// ❌ 不好:处理所有元素
boolean hasEven = numbers.stream()
    .filter(n -> n % 2 == 0)
    .collect(Collectors.toList())
    .size() > 0;

// ✓ 好:找到第一个就停止
boolean hasEven2 = numbers.stream()
    .anyMatch(n -> n % 2 == 0);

9.3 选择合适的集合类型

// ✓ 好:ArrayList适合随机访问和并行处理
List<Integer> arrayList = new ArrayList<>(numbers);
long sum1 = arrayList.parallelStream()
    .mapToInt(Integer::intValue)
    .sum();

// ❌ 不好:LinkedList不适合并行处理
List<Integer> linkedList = new LinkedList<>(numbers);
long sum2 = linkedList.parallelStream()
    .mapToInt(Integer::intValue)
    .sum();

9.4 使用数值流

List<Integer> numbers = getNumbers();

// ❌ 不好:装箱拆箱开销
int sum1 = numbers.stream()
    .reduce(0, Integer::sum);

// ✓ 好:使用数值流
int sum2 = numbers.stream()
    .mapToInt(Integer::intValue)
    .sum();

9.5 合理使用并行流

List<Integer> numbers = getNumbers();

// 小数据量:使用串行流
if (numbers.size() < 10000) {
    int sum = numbers.stream()
        .mapToInt(Integer::intValue)
        .sum();
}

// 大数据量:使用并行流
if (numbers.size() >= 10000) {
    int sum = numbers.parallelStream()
        .mapToInt(Integer::intValue)
        .sum();
}

十、常见问题与解决方案

10.1 Stream已关闭异常

// ❌ 错误:Stream只能使用一次
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
long count = stream.count();
long sum = stream.mapToInt(Integer::intValue).sum();  // 异常!

// ✓ 正确:重新创建Stream
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
long count2 = numbers.stream().count();
long sum2 = numbers.stream().mapToInt(Integer::intValue).sum();

10.2 空指针异常

List<User> users = getUserList();

// ❌ 可能空指针
List<String> names = users.stream()
    .map(User::getName)  // 如果getName()返回null
    .map(String::toUpperCase)  // 空指针异常!
    .collect(Collectors.toList());

// ✓ 正确:过滤null
List<String> names2 = users.stream()
    .map(User::getName)
    .filter(Objects::nonNull)
    .map(String::toUpperCase)
    .collect(Collectors.toList());

10.3 并发修改异常

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));

// ❌ 错误:遍历时修改原集合
numbers.stream()
    .forEach(n -> {
        if (n % 2 == 0) {
            numbers.remove(n);  // ConcurrentModificationException
        }
    });

// ✓ 正确:创建新集合
List<Integer> oddNumbers = numbers.stream()
    .filter(n -> n % 2 != 0)
    .collect(Collectors.toList());

10.4 类型转换异常

List<Object> objects = Arrays.asList("1", "2", "3", 4, 5);

// ❌ 错误:类型不一致
List<String> strings = objects.stream()
    .map(obj -> (String) obj)  // ClassCastException
    .collect(Collectors.toList());

// ✓ 正确:类型检查
List<String> strings2 = objects.stream()
    .filter(obj -> obj instanceof String)
    .map(obj -> (String) obj)
    .collect(Collectors.toList());

十一、Stream API速查表

┌────────────────────────────────────────────────────────────────────────────┐
│                          Stream API 速查表                                  │
├────────────────────────────────────────────────────────────────────────────┤
│ 创建Stream                                                                  │
├────────────────────────────────────────────────────────────────────────────┤
│ list.stream()                          从集合创建                           │
│ list.parallelStream()                  创建并行流                           │
│ Arrays.stream(array)                   从数组创建                           │
│ Stream.of(1, 2, 3)                     直接创建                            │
│ Stream.empty()                         创建空流                            │
│ Stream.generate(() -> "Hello")         生成无限流                          │
│ Stream.iterate(0, n -> n + 1)          迭代生成                            │
│ IntStream.range(1, 10)                 数值范围(1-9)                       │
│ IntStream.rangeClosed(1, 10)           数值范围(1-10)                      │
│ Files.lines(path)                      从文件创建                          │
├────────────────────────────────────────────────────────────────────────────┤
│ 中间操作(返回Stream)                                                      │
├────────────────────────────────────────────────────────────────────────────┤
│ filter(predicate)                      过滤元素                            │
│ map(function)                          映射转换                            │
│ flatMap(function)                      扁平化映射                          │
│ distinct()                             去重                                │
│ sorted()                               自然排序                            │
│ sorted(comparator)                     自定义排序                          │
│ limit(n)                               取前n个                             │
│ skip(n)                                跳过前n个                           │
│ peek(consumer)                         查看元素(调试用)                   │
├────────────────────────────────────────────────────────────────────────────┤
│ 终止操作(触发执行)                                                        │
├────────────────────────────────────────────────────────────────────────────┤
│ collect(collector)                     收集结果                            │
│ forEach(consumer)                      遍历元素                            │
│ reduce(identity, accumulator)          归约                                │
│ count()                                计数                                │
│ min(comparator)                        最小值                              │
│ max(comparator)                        最大值                              │
│ anyMatch(predicate)                    任意匹配                            │
│ allMatch(predicate)                    全部匹配                            │
│ noneMatch(predicate)                   全不匹配                            │
│ findFirst()                            查找第一个                          │
│ findAny()                              查找任意一个                        │
├────────────────────────────────────────────────────────────────────────────┤
│ Collectors常用方法                                                          │
├────────────────────────────────────────────────────────────────────────────┤
│ toList()                               转为List                            │
│ toSet()                                转为Set                             │
│ toMap(keyMapper, valueMapper)          转为Map                             │
│ joining()                              拼接字符串                          │
│ joining(delimiter)                     带分隔符拼接                        │
│ groupingBy(classifier)                 分组                                │
│ partitioningBy(predicate)              分区                                │
│ counting()                             计数                                │
│ summingInt(mapper)                     求和                                │
│ averagingInt(mapper)                   平均值                              │
│ maxBy(comparator)                      最大值                              │
│ minBy(comparator)                      最小值                              │
│ summarizingInt(mapper)                 综合统计                            │
├────────────────────────────────────────────────────────────────────────────┤
│ 数值流方法                                                                  │
├────────────────────────────────────────────────────────────────────────────┤
│ mapToInt(mapper)                       转为IntStream                       │
│ mapToLong(mapper)                      转为LongStream                      │
│ mapToDouble(mapper)                    转为DoubleStream                    │
│ sum()                                  求和                                │
│ average()                              平均值                              │
│ max()                                  最大值                              │
│ min()                                  最小值                              │
│ summaryStatistics()                    综合统计                            │
│ boxed()                                装箱为对象流                        │
└────────────────────────────────────────────────────────────────────────────┘

十二、Lambda表达式速查表

┌────────────────────────────────────────────────────────────────────────────┐
│                        Lambda表达式速查表                                   │
├────────────────────────────────────────────────────────────────────────────┤
│ 语法格式                                                                    │
├────────────────────────────────────────────────────────────────────────────┤
│ () -> expression                       无参数                              │
│ (param) -> expression                  单参数                              │
│ (param1, param2) -> expression         多参数                              │
│ (param) -> { statements; }             代码块                              │
├────────────────────────────────────────────────────────────────────────────┤
│ 函数式接口                                                                  │
├────────────────────────────────────────────────────────────────────────────┤
│ Consumer<T>                            void accept(T t)                    │
│   示例: s -> System.out.println(s)                                         │
│                                                                            │
│ Supplier<T>                            T get()                             │
│   示例: () -> "Hello"                                                      │
│                                                                            │
│ Function<T,R>                          R apply(T t)                        │
│   示例: s -> s.length()                                                    │
│                                                                            │
│ Predicate<T>                           boolean test(T t)                   │
│   示例: n -> n > 0                                                         │
│                                                                            │
│ BiFunction<T,U,R>                      R apply(T t, U u)                   │
│   示例: (a, b) -> a + b                                                    │
│                                                                            │
│ UnaryOperator<T>                       T apply(T t)                        │
│   示例: s -> s.toUpperCase()                                               │
│                                                                            │
│ BinaryOperator<T>                      T apply(T t1, T t2)                 │
│   示例: (a, b) -> a * b                                                    │
├────────────────────────────────────────────────────────────────────────────┤
│ 方法引用                                                                    │
├────────────────────────────────────────────────────────────────────────────┤
│ 静态方法引用                           类名::静态方法名                     │
│   示例: Integer::parseInt                                                  │
│   等价: s -> Integer.parseInt(s)                                           │
│                                                                            │
│ 实例方法引用                           对象::实例方法名                     │
│   示例: str::toUpperCase                                                   │
│   等价: () -> str.toUpperCase()                                            │
│                                                                            │
│ 类的实例方法引用                       类名::实例方法名                     │
│   示例: String::length                                                     │
│   等价: s -> s.length()                                                    │
│                                                                            │
│ 构造方法引用                           类名::new                            │
│   示例: ArrayList::new                                                     │
│   等价: () -> new ArrayList<>()                                            │
└────────────────────────────────────────────────────────────────────────────┘

十三、实战练习题

练习1:基础操作

/**
 * 给定一个整数列表,完成以下操作:
 * 1. 过滤出所有偶数
 * 2. 将每个数乘以2
 * 3. 按降序排序
 * 4. 取前5个
 * 5. 转换为List
 */
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);

// 你的代码:
List<Integer> result = numbers.stream()
    .filter(n -> n % 2 == 0)
    .map(n -> n * 2)
    .sorted(Comparator.reverseOrder())
    .limit(5)
    .collect(Collectors.toList());

System.out.println(result);  // [24, 20, 16, 12, 8]

练习2:字符串处理

/**
 * 给定一个字符串列表,完成以下操作:
 * 1. 过滤出长度大于3的字符串
 * 2. 转换为大写
 * 3. 去重
 * 4. 按字母顺序排序
 * 5. 用逗号拼接
 */
List<String> words = Arrays.asList("apple", "banana", "cat", "dog", "elephant", "cat", "apple");

// 你的代码:
String result = words.stream()
    .filter(word -> word.length() > 3)
    .map(String::toUpperCase)
    .distinct()
    .sorted()
    .collect(Collectors.joining(", "));

System.out.println(result);  // "APPLE, BANANA, ELEPHANT"

练习3:对象操作

/**
 * 给定用户列表,完成以下操作:
 * 1. 找出年龄大于25岁的用户
 * 2. 按年龄降序排序
 * 3. 提取用户名
 * 4. 转换为List
 */
class User {
    private String name;
    private int age;
    // 构造方法、getter/setter
}

List<User> users = Arrays.asList(
    new User("Alice", 30),
    new User("Bob", 25),
    new User("Charlie", 35),
    new User("Diana", 28)
);

// 你的代码:
List<String> result = users.stream()
    .filter(user -> user.getAge() > 25)
    .sorted(Comparator.comparing(User::getAge).reversed())
    .map(User::getName)
    .collect(Collectors.toList());

System.out.println(result);  // ["Charlie", "Alice", "Diana"]

练习4:分组统计

/**
 * 给定订单列表,完成以下操作:
 * 1. 按状态分组
 * 2. 统计每个状态的订单数量
 * 3. 计算每个状态的总金额
 */
class Order {
    private String status;
    private BigDecimal amount;
    // 构造方法、getter/setter
}

List<Order> orders = Arrays.asList(
    new Order("PENDING", new BigDecimal("100")),
    new Order("COMPLETED", new BigDecimal("200")),
    new Order("PENDING", new BigDecimal("150")),
    new Order("COMPLETED", new BigDecimal("300")),
    new Order("CANCELLED", new BigDecimal("50"))
);

// 你的代码:
// 1. 按状态分组统计数量
Map<String, Long> countByStatus = orders.stream()
    .collect(Collectors.groupingBy(
        Order::getStatus,
        Collectors.counting()
    ));

// 2. 按状态分组计算总金额
Map<String, BigDecimal> amountByStatus = orders.stream()
    .collect(Collectors.groupingBy(
        Order::getStatus,
        Collectors.reducing(
            BigDecimal.ZERO,
            Order::getAmount,
            BigDecimal::add
        )
    ));

System.out.println(countByStatus);
// {PENDING=2, COMPLETED=2, CANCELLED=1}

System.out.println(amountByStatus);
// {PENDING=250, COMPLETED=500, CANCELLED=50}

练习5:复杂数据处理

/**
 * 给定学生成绩列表,完成以下操作:
 * 1. 计算每个学生的平均分
 * 2. 找出平均分最高的学生
 * 3. 统计及格(>=60分)的科目数
 */
class Score {
    private String studentName;
    private String subject;
    private int score;
    // 构造方法、getter/setter
}

List<Score> scores = Arrays.asList(
    new Score("Alice", "Math", 90),
    new Score("Alice", "English", 85),
    new Score("Alice", "Physics", 88),
    new Score("Bob", "Math", 75),
    new Score("Bob", "English", 80),
    new Score("Bob", "Physics", 70),
    new Score("Charlie", "Math", 95),
    new Score("Charlie", "English", 92),
    new Score("Charlie", "Physics", 98)
);

// 你的代码:
// 1. 计算每个学生的平均分
Map<String, Double> avgScores = scores.stream()
    .collect(Collectors.groupingBy(
        Score::getStudentName,
        Collectors.averagingInt(Score::getScore)
    ));

System.out.println(avgScores);
// {Alice=87.67, Bob=75.0, Charlie=95.0}

// 2. 找出平均分最高的学生
Optional<Map.Entry<String, Double>> topStudent = avgScores.entrySet().stream()
    .max(Map.Entry.comparingByValue());

topStudent.ifPresent(entry -> 
    System.out.println("最高分学生: " + entry.getKey() + ", 平均分: " + entry.getValue())
);
// 最高分学生: Charlie, 平均分: 95.0

// 3. 统计每个学生及格的科目数
Map<String, Long> passCount = scores.stream()
    .filter(score -> score.getScore() >= 60)
    .collect(Collectors.groupingBy(
        Score::getStudentName,
        Collectors.counting()
    ));

System.out.println(passCount);
// {Alice=3, Bob=3, Charlie=3}

练习6:扁平化处理

/**
 * 给定部门列表,每个部门有多个员工,完成以下操作:
 * 1. 获取所有员工列表
 * 2. 找出工资最高的员工
 * 3. 按部门统计员工数量
 */
class Department {
    private String name;
    private List<Employee> employees;
    // 构造方法、getter/setter
}

class Employee {
    private String name;
    private BigDecimal salary;
    private String department;
    // 构造方法、getter/setter
}

List<Department> departments = Arrays.asList(
    new Department("IT", Arrays.asList(
        new Employee("Alice", new BigDecimal("10000"), "IT"),
        new Employee("Bob", new BigDecimal("12000"), "IT")
    )),
    new Department("HR", Arrays.asList(
        new Employee("Charlie", new BigDecimal("8000"), "HR"),
        new Employee("Diana", new BigDecimal("9000"), "HR")
    ))
);

// 你的代码:
// 1. 获取所有员工
List<Employee> allEmployees = departments.stream()
    .flatMap(dept -> dept.getEmployees().stream())
    .collect(Collectors.toList());

// 2. 找出工资最高的员工
Optional<Employee> highestPaid = allEmployees.stream()
    .max(Comparator.comparing(Employee::getSalary));

highestPaid.ifPresent(emp -> 
    System.out.println("最高工资员工: " + emp.getName() + ", 工资: " + emp.getSalary())
);
// 最高工资员工: Bob, 工资: 12000

// 3. 按部门统计员工数量
Map<String, Long> empCountByDept = allEmployees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDepartment,
        Collectors.counting()
    ));

System.out.println(empCountByDept);
// {IT=2, HR=2}

练习7:数据转换

/**
 * 给定CSV格式的字符串列表,解析并转换为对象
 * 格式:name,age,city
 */
List<String> csvLines = Arrays.asList(
    "Alice,25,Beijing",
    "Bob,30,Shanghai",
    "Charlie,28,Guangzhou",
    "Diana,35,Shenzhen"
);

class Person {
    private String name;
    private int age;
    private String city;
    // 构造方法、getter/setter
    
    public static Person fromCsv(String csv) {
        String[] parts = csv.split(",");
        return new Person(parts[0], Integer.parseInt(parts[1]), parts[2]);
    }
}

// 你的代码:
List<Person> persons = csvLines.stream()
    .map(Person::fromCsv)
    .collect(Collectors.toList());

// 找出年龄大于25岁且在一线城市的人
List<String> result = persons.stream()
    .filter(p -> p.getAge() > 25)
    .filter(p -> Arrays.asList("Beijing", "Shanghai", "Guangzhou", "Shenzhen")
                       .contains(p.getCity()))
    .map(Person::getName)
    .collect(Collectors.toList());

System.out.println(result);  // ["Bob", "Charlie", "Diana"]

练习8:性能优化

/**
 * 优化以下代码,使其性能更好
 */
List<Integer> numbers = IntStream.rangeClosed(1, 1000000)
    .boxed()
    .collect(Collectors.toList());

// ❌ 原始代码(性能差)
long start1 = System.currentTimeMillis();
List<Integer> result1 = numbers.stream()
    .filter(n -> n % 2 == 0)
    .map(n -> n * 2)
    .filter(n -> n > 1000)
    .sorted()
    .limit(100)
    .collect(Collectors.toList());
long end1 = System.currentTimeMillis();
System.out.println("原始代码耗时: " + (end1 - start1) + "ms");

// ✓ 优化后代码
long start2 = System.currentTimeMillis();
List<Integer> result2 = numbers.stream()
    .filter(n -> n % 2 == 0)
    .filter(n -> n * 2 > 1000)  // 提前过滤,减少后续处理
    .map(n -> n * 2)
    .sorted()
    .limit(100)  // 提前限制数量
    .collect(Collectors.toList());
long end2 = System.currentTimeMillis();
System.out.println("优化后耗时: " + (end2 - start2) + "ms");

// ✓ 使用并行流(数据量大时)
long start3 = System.currentTimeMillis();
List<Integer> result3 = numbers.parallelStream()
    .filter(n -> n % 2 == 0)
    .filter(n -> n * 2 > 1000)
    .map(n -> n * 2)
    .sorted()
    .limit(100)
    .collect(Collectors.toList());
long end3 = System.currentTimeMillis();
System.out.println("并行流耗时: " + (end3 - start3) + "ms");