JDK 8 Stream API 教程文档

Stream 简介

Stream 是 JDK 8 中处理集合(Collection)数据的新抽象,它可以让你以声明式的方式处理数据,类似于使用 SQL 语句进行数据库查询。

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

java 复制代码
// 传统方式
List<String> result = new ArrayList<>();
for (String name : names) {
    if (name.startsWith("A")) {
        result.add(name.toUpperCase());
    }
}

// Stream 方式
List<String> streamResult = names.stream()
    .filter(name -> name.startsWith("A"))
    .map(String::toUpperCase)
    .collect(Collectors.toList());

Stream 的特点

  1. 不是数据结构:Stream 不会存储数据,只是对数据源进行计算
  2. 不修改源数据:对 Stream 的操作不会影响原始数据源
  3. 惰性执行:中间操作是惰性的,只有在终止操作时才会执行
  4. 可消费性:Stream 只能被消费一次

Stream 操作分类

操作类型 方法示例 返回值 特点
中间操作 filter(), map(), sorted() Stream 惰性执行,可链式调用
终止操作 forEach(), collect(), count() 非Stream 立即执行,关闭流

创建 Stream

1. 从集合创建

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

2. 从数组创建

java 复制代码
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);

3. 使用 Stream.of()

java 复制代码
Stream<String> stream = Stream.of("a", "b", "c");

4. 创建数值流

java 复制代码
IntStream intStream = IntStream.range(1, 5);     // 1,2,3,4
IntStream closedStream = IntStream.rangeClosed(1, 5); // 1,2,3,4,5

5. 创建无限流

java 复制代码
// 生成10个随机数
Stream<Double> randomStream = Stream.generate(Math::random).limit(10);

// 创建斐波那契数列
Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0] + t[1]})
      .limit(10)
      .map(t -> t[0])
      .forEach(System.out::println);

中间操作

1. filter() - 过滤

java 复制代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
List<String> result = names.stream()
    .filter(name -> name.length() > 3)
    .collect(Collectors.toList());
// 结果: [Alice, Charlie, David]

2. map() - 映射

java 复制代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<Integer> nameLengths = names.stream()
    .map(String::length)
    .collect(Collectors.toList());
// 结果: [5, 3, 7]

3. flatMap() - 扁平化映射

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

List<String> flatList = listOfLists.stream()
    .flatMap(List::stream)
    .collect(Collectors.toList());
// 结果: [a, b, c, d]

4. distinct() - 去重

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3);
List<Integer> distinctNumbers = numbers.stream()
    .distinct()
    .collect(Collectors.toList());
// 结果: [1, 2, 3]

5. sorted() - 排序

java 复制代码
List<String> names = Arrays.asList("Charlie", "Alice", "Bob");
List<String> sortedNames = names.stream()
    .sorted()
    .collect(Collectors.toList());
// 结果: [Alice, Bob, Charlie]

// 自定义排序
List<String> customSorted = names.stream()
    .sorted((a, b) -> b.length() - a.length())
    .collect(Collectors.toList());
// 结果: [Charlie, Alice, Bob]

6. limit() 和 skip() - 限制和跳过

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

List<Integer> limited = numbers.stream()
    .limit(5)
    .collect(Collectors.toList());
// 结果: [1, 2, 3, 4, 5]

List<Integer> skipped = numbers.stream()
    .skip(5)
    .collect(Collectors.toList());
// 结果: [6, 7, 8, 9, 10]

7. peek() - 查看元素(主要用于调试)

java 复制代码
List<String> result = Stream.of("one", "two", "three")
    .filter(s -> s.length() > 3)
    .peek(s -> System.out.println("Filtered value: " + s))
    .map(String::toUpperCase)
    .peek(s -> System.out.println("Mapped value: " + s))
    .collect(Collectors.toList());

终止操作

1. forEach() - 遍历

java 复制代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream().forEach(System.out::println);

2. collect() - 收集

java 复制代码
// 转换为List
List<String> list = stream.collect(Collectors.toList());

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

// 转换为Map
Map<String, Integer> map = stream.collect(
    Collectors.toMap(s -> s, String::length)
);

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

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

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

3. reduce() - 归约

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

// 求和
Optional<Integer> sum = numbers.stream().reduce(Integer::sum);
// 或
Integer sum2 = numbers.stream().reduce(0, Integer::sum);

// 求最大值
Optional<Integer> max = numbers.stream().reduce(Integer::max);

// 字符串连接
Optional<String> concat = Stream.of("a", "b", "c").reduce(String::concat);

4. 匹配操作

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

boolean allEven = numbers.stream().allMatch(n -> n % 2 == 0); // false
boolean anyEven = numbers.stream().anyMatch(n -> n % 2 == 0); // true
boolean noneEven = numbers.stream().noneMatch(n -> n % 2 == 0); // false

5. 查找操作

java 复制代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

Optional<String> first = names.stream().findFirst();
Optional<String> any = names.stream().findAny();

6. 统计操作

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

long count = numbers.stream().count();
IntSummaryStatistics stats = numbers.stream()
    .mapToInt(Integer::intValue)
    .summaryStatistics();

System.out.println("最大值: " + stats.getMax());
System.out.println("最小值: " + stats.getMin());
System.out.println("总和: " + stats.getSum());
System.out.println("平均值: " + stats.getAverage());

并行流

创建并行流

java 复制代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// 方法1:从集合创建
Stream<String> parallelStream = names.parallelStream();

// 方法2:将顺序流转为并行流
Stream<String> parallel = names.stream().parallel();

并行流示例

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

long startTime = System.currentTimeMillis();
long sum = numbers.parallelStream()
    .mapToLong(Integer::longValue)
    .sum();
long endTime = System.currentTimeMillis();

System.out.println("总和: " + sum);
System.out.println("执行时间: " + (endTime - startTime) + "ms");

并行流注意事项

  1. 线程安全:确保操作是线程安全的
  2. 状态无关:避免有状态的lambda表达式
  3. 顺序敏感:某些操作(如findFirst)在并行流中性能可能更差
  4. 数据量:小数据量使用并行流可能适得其反

实战示例

示例1:员工数据处理

java 复制代码
class Employee {
    private String name;
    private String department;
    private double salary;
    private int age;
    
    // 构造方法、getter、setter省略
}

List<Employee> employees = Arrays.asList(
    new Employee("Alice", "IT", 5000, 25),
    new Employee("Bob", "HR", 4000, 30),
    new Employee("Charlie", "IT", 6000, 35),
    new Employee("David", "Finance", 5500, 28)
);

// 1. 按部门分组,计算每个部门的平均工资
Map<String, Double> avgSalaryByDept = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDepartment,
        Collectors.averagingDouble(Employee::getSalary)
    ));

// 2. 找出IT部门工资最高的员工
Optional<Employee> highestPaidInIT = employees.stream()
    .filter(e -> "IT".equals(e.getDepartment()))
    .max(Comparator.comparingDouble(Employee::getSalary));

// 3. 按年龄排序,获取前3名员工
List<Employee> top3ByAge = employees.stream()
    .sorted(Comparator.comparingInt(Employee::getAge).reversed())
    .limit(3)
    .collect(Collectors.toList());

// 4. 统计每个部门的员工数量
Map<String, Long> employeeCountByDept = employees.stream()
    .collect(Collectors.groupingBy(
        Employee::getDepartment, 
        Collectors.counting()
    ));

示例2:文件处理

java 复制代码
// 读取文件并统计单词频率
try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {
    Map<String, Long> wordCount = lines
        .flatMap(line -> Arrays.stream(line.split("\s+")))
        .map(word -> word.replaceAll("[^a-zA-Z]", "").toLowerCase())
        .filter(word -> !word.isEmpty())
        .collect(Collectors.groupingBy(
            word -> word, 
            Collectors.counting()
        ));
    
    // 按频率排序
    wordCount.entrySet().stream()
        .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
        .limit(10)
        .forEach(entry -> 
            System.out.println(entry.getKey() + ": " + entry.getValue())
        );
} catch (IOException e) {
    e.printStackTrace();
}

示例3:复杂数据转换

java 复制代码
// 多层数据转换和聚合
class Order {
    private String customer;
    private List<OrderItem> items;
    private LocalDate orderDate;
    // 构造方法、getter、setter省略
}

class OrderItem {
    private String product;
    private int quantity;
    private double price;
    // 构造方法、getter、setter省略
}

List<Order> orders = // 初始化订单数据

// 计算每个客户的总消费金额
Map<String, Double> customerTotal = orders.stream()
    .collect(Collectors.groupingBy(
        Order::getCustomer,
        Collectors.summingDouble(order -> 
            order.getItems().stream()
                .mapToDouble(item -> item.getQuantity() * item.getPrice())
                .sum()
        )
    ));

// 找出最畅销的产品
Optional<String> bestSellingProduct = orders.stream()
    .flatMap(order -> order.getItems().stream())
    .collect(Collectors.groupingBy(
        OrderItem::getProduct,
        Collectors.summingInt(OrderItem::getQuantity)
    ))
    .entrySet().stream()
    .max(Map.Entry.comparingByValue())
    .map(Map.Entry::getKey);

最佳实践

  1. 优先使用方法引用:使代码更简洁
  2. 避免副作用:保持函数的纯粹性
  3. 合理使用并行流:根据数据量和操作复杂度决定
  4. 及时关闭资源:使用try-with-resources处理IO相关的Stream
  5. 合理使用Optional:避免空指针异常

总结

JDK 8 的 Stream API 为集合操作提供了强大的函数式编程能力,通过链式调用和惰性求值等特性,可以写出更简洁、易读、高效的代码。掌握 Stream API 对于现代 Java 开发至关重要。

相关推荐
火山引擎开发者社区2 小时前
Vibe Coze-企业 AI 应用赛道开启
后端
百锦再2 小时前
大型省级政务平台采用金仓数据库(KingbaseES)
开发语言·数据库·后端·rust·eclipse
m0_639817152 小时前
基于springboot个人云盘管理系统【带源码和文档】
java·spring boot·后端
NUllPOINTEXCEPTION3 小时前
MyBatis-Plus 开发指北
后端
bcbnb3 小时前
没有 Mac,如何上架 iOS App?跨平台开发者的完整上架实战指南
后端
ZhengEnCi3 小时前
P3E-Python Lambda表达式完全指南-什么是匿名函数?为什么90%程序员都在用?怎么快速掌握函数式编程利器?
后端·python
爱分享的鱼鱼3 小时前
Java基础 (七:网络编程、HTTP客户端开发)(待完善)
后端
aiopencode4 小时前
iOS WebKit Debug Proxy 深度解析与替代方案,跨平台远程调试的新思路
后端
Java小混子4 小时前
golang项目CRUD示例
开发语言·后端·golang