Stream API核心操作详解
Stream概述
Stream API是Java 8引入的用于处理集合数据的高级抽象。它提供了一种声明式的数据处理方式,类似于SQL语句对数据库的查询操作,让开发者能够专注于"做什么"而非"怎么做"。
Stream可以被理解为数据管道:数据从源头流入,经过一系列中间处理节点(过滤、转换、排序等),最终产生结果输出。
flowchart LR
subgraph Stream处理流程
A[(数据源)] --> B[创建Stream]
B --> C[中间操作1]
C --> D[中间操作2]
D --> E[中间操作N]
E --> F[终端操作]
F --> G[结果输出]
end
style A fill:#e3f2fd,stroke:#1565c0,rx:10,ry:10
style F fill:#fff3e0,stroke:#ef6c00,rx:10,ry:10
style G fill:#e8f5e9,stroke:#388e3c,rx:10,ry:10Stream的核心特性
非存储性
Stream本身不存储数据,它只是数据源的一个视图。数据源可以是集合、数组、I/O通道或生成器函数。
函数式风格
对Stream的任何操作都不会改变原始数据源。例如,过滤操作不会删除原集合中的元素,而是生成一个不包含被过滤元素的新Stream。
惰性求值
Stream的中间操作是延迟执行的,只有遇到终端操作时才会真正执行所有操作。这种机制可以实现高效的短路求值。
一次性消费
Stream只能被遍历一次,遍历完成后Stream即失效。如需再次操作,必须从数据源重新创建Stream。
flowchart TB
subgraph Stream特性
A[非存储性] --> A1[不持有数据<br/>只是视图]
B[函数式] --> B1[不修改源数据<br/>生成新流]
C[惰性求值] --> C1[延迟执行<br/>遇到终端操作才计算]
D[一次消费] --> D1[只能遍历一次<br/>用完即失效]
end
style A fill:#e8eaf6,stroke:#3f51b5,rx:10,ry:10
style B fill:#fce4ec,stroke:#c2185b,rx:10,ry:10
style C fill:#e0f7fa,stroke:#00838f,rx:10,ry:10
style D fill:#fff8e1,stroke:#ff8f00,rx:10,ry:10创建Stream的多种方式
从集合创建
最常用的方式是通过集合的stream()方法创建:
// 员工数据列表
List<Employee> employees = Arrays.asList(
new Employee("张三", 28, 15000),
new Employee("李四", 32, 22000),
new Employee("王五", 25, 12000)
);
// 创建串行流
Stream<Employee> stream = employees.stream();
// 创建并行流
Stream<Employee> parallelStream = employees.parallelStream();使用Stream.of静态方法
适合快速创建少量元素的Stream:
Stream<String> departmentStream = Stream.of("研发部", "市场部", "财务部", "人事部");从数组创建
Integer[] salaryLevels = {5000, 10000, 15000, 20000, 30000};
Stream<Integer> salaryStream = Arrays.stream(salaryLevels);使用生成器创建
// 生成无限流(需配合limit使用)
Stream<Double> randomStream = Stream.generate(Math::random).limit(5);
// 迭代生成
Stream<Integer> iterateStream = Stream.iterate(1, n -> n + 1).limit(10);中间操作详解
中间操作不会立即执行,而是返回一个新的Stream,支持链式调用。
flowchart LR
subgraph 常用中间操作
A[filter过滤] --> B[map映射]
B --> C[sorted排序]
C --> D[distinct去重]
D --> E[limit截取]
E --> F[skip跳过]
F --> G[flatMap扁平化]
end
style A fill:#e3f2fd,stroke:#1565c0,rx:10,ry:10
style B fill:#f3e5f5,stroke:#7b1fa2,rx:10,ry:10
style C fill:#e8f5e9,stroke:#388e3c,rx:10,ry:10
style D fill:#fff3e0,stroke:#ef6c00,rx:10,ry:10filter - 条件过滤
根据指定条件筛选元素:
List<Product> products = getProductList();
// 筛选价格大于100且库存充足的商品
List<Product> availableProducts = products.stream()
.filter(p -> p.getPrice() > 100)
.filter(p -> p.getStock() > 0)
.collect(Collectors.toList());map - 元素转换
将每个元素按规则转换为新元素:
List<Order> orders = getOrderList();
// 提取所有订单的金额
List<BigDecimal> amounts = orders.stream()
.map(Order::getTotalAmount)
.collect(Collectors.toList());
// 将金额转换为分(整数)
List<Long> amountInCents = orders.stream()
.map(order -> order.getTotalAmount().multiply(new BigDecimal("100")).longValue())
.collect(Collectors.toList());flatMap - 扁平化映射
将嵌套结构展开为单层Stream:
List<Department> departments = getDepartmentList();
// 获取所有部门的所有员工(每个部门有多个员工)
List<Employee> allEmployees = departments.stream()
.flatMap(dept -> dept.getEmployees().stream())
.collect(Collectors.toList());flowchart TB
subgraph flatMap示意
A[部门列表] --> B[研发部]
A --> C[市场部]
B --> B1[员工1]
B --> B2[员工2]
C --> C1[员工3]
C --> C2[员工4]
B1 --> D[扁平化后的员工列表]
B2 --> D
C1 --> D
C2 --> D
end
style A fill:#e3f2fd,stroke:#1565c0,rx:10,ry:10
style D fill:#e8f5e9,stroke:#388e3c,rx:10,ry:10sorted - 排序
支持自然排序和自定义排序:
List<Employee> employees = getEmployeeList();
// 按薪资升序排序
List<Employee> sortedBySalary = employees.stream()
.sorted(Comparator.comparing(Employee::getSalary))
.collect(Collectors.toList());
// 按年龄降序,年龄相同则按姓名升序
List<Employee> complexSort = employees.stream()
.sorted(Comparator.comparing(Employee::getAge).reversed()
.thenComparing(Employee::getName))
.collect(Collectors.toList());distinct - 去重
基于元素的equals()方法去除重复:
List<String> categories = Arrays.asList("电子", "服装", "电子", "食品", "服装", "图书");
List<String> uniqueCategories = categories.stream()
.distinct()
.collect(Collectors.toList());
// 结果: [电子, 服装, 食品, 图书]limit与skip - 分页处理
List<Article> articles = getArticleList();
// 获取第2页数据(每页10条)
int pageSize = 10;
int pageNum = 2;
List<Article> page2 = articles.stream()
.skip((pageNum - 1) * pageSize) // 跳过前10条
.limit(pageSize) // 取10条
.collect(Collectors.toList());组合使用示例
实现一个复杂的数据处理流程:
List<Transaction> transactions = getTransactionList();
// 查询:最近30天内,金额大于1000的交易,按时间倒序,取前5条
List<Transaction> result = transactions.stream()
.filter(t -> t.getTransactionTime().isAfter(LocalDateTime.now().minusDays(30)))
.filter(t -> t.getAmount().compareTo(new BigDecimal("1000")) > 0)
.sorted(Comparator.comparing(Transaction::getTransactionTime).reversed())
.limit(5)
.collect(Collectors.toList());flowchart LR
A[交易数据] --> B[filter<br/>近30天]
B --> C[filter<br/>金额>1000]
C --> D[sorted<br/>时间倒序]
D --> E[limit 5]
E --> F[收集结果]
style A fill:#e3f2fd,stroke:#1565c0,rx:10,ry:10
style F fill:#e8f5e9,stroke:#388e3c,rx:10,ry:10终端操作详解
终端操作会触发Stream的实际执行,并产生最终结果。一旦执行终端操作,Stream即被消费完毕。
forEach - 遍历消费
List<Notification> notifications = getNotificationList();
// 发送所有通知
notifications.stream()
.filter(Notification::isEnabled)
.forEach(n -> notificationService.send(n));collect - 结果收集
最常用的终端操作,可以将Stream转换为各种集合类型:
List<User> users = getUserList();
// 收集为List
List<String> nameList = users.stream()
.map(User::getName)
.collect(Collectors.toList());
// 收集为Set(自动去重)
Set<String> citySet = users.stream()
.map(User::getCity)
.collect(Collectors.toSet());
// 收集为Map(用户ID -> 用户对象)
Map<Long, User> userMap = users.stream()
.collect(Collectors.toMap(User::getId, u -> u));
// 按城市分组
Map<String, List<User>> usersByCity = users.stream()
.collect(Collectors.groupingBy(User::getCity));
// 按是否VIP分区
Map<Boolean, List<User>> partitioned = users.stream()
.collect(Collectors.partitioningBy(User::isVip));reduce - 归约计算
将Stream中的元素归约为单一值:
List<OrderItem> items = getOrderItems();
// 计算订单总金额
BigDecimal totalAmount = items.stream()
.map(item -> item.getPrice().multiply(new BigDecimal(item.getQuantity())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 找出最大值
Optional<Integer> maxQuantity = items.stream()
.map(OrderItem::getQuantity)
.reduce(Integer::max);count/max/min - 统计操作
List<Score> scores = getScoreList();
// 统计数量
long count = scores.stream()
.filter(s -> s.getValue() >= 60)
.count();
// 找最高分
Optional<Score> maxScore = scores.stream()
.max(Comparator.comparing(Score::getValue));
// 找最低分
Optional<Score> minScore = scores.stream()
.min(Comparator.comparing(Score::getValue));anyMatch/allMatch/noneMatch - 匹配判断
List<Task> tasks = getTaskList();
// 是否存在未完成的任务
boolean hasUnfinished = tasks.stream()
.anyMatch(t -> !t.isCompleted());
// 是否所有任务都已分配
boolean allAssigned = tasks.stream()
.allMatch(t -> t.getAssignee() != null);
// 是否没有逾期任务
boolean noOverdue = tasks.stream()
.noneMatch(t -> t.getDueDate().isBefore(LocalDate.now()));findFirst/findAny - 查找元素
List<Coupon> coupons = getCouponList();
// 查找第一个可用优惠券
Optional<Coupon> firstAvailable = coupons.stream()
.filter(Coupon::isValid)
.filter(c -> c.getMinAmount().compareTo(orderAmount) <= 0)
.findFirst();
// 并行流中任意匹配的元素(性能更好)
Optional<Coupon> anyAvailable = coupons.parallelStream()
.filter(Coupon::isValid)
.findAny();完整处理流程示例
flowchart TB
subgraph 员工薪资分析
A[(员工数据)] --> B[stream创建]
B --> C[filter<br/>在职员工]
C --> D[filter<br/>入职满一年]
D --> E[map<br/>提取薪资]
E --> F[sorted<br/>薪资排序]
F --> G[limit<br/>取前10]
G --> H[collect<br/>收集为List]
H --> I[结果:高薪Top10]
end
style A fill:#e3f2fd,stroke:#1565c0,rx:10,ry:10
style H fill:#fff3e0,stroke:#ef6c00,rx:10,ry:10
style I fill:#e8f5e9,stroke:#388e3c,rx:10,ry:10public class SalaryAnalyzer {
public List<Employee> getTopSalaryEmployees(List<Employee> employees, int topN) {
LocalDate oneYearAgo = LocalDate.now().minusYears(1);
return employees.stream()
// 过滤在职员工
.filter(e -> "ACTIVE".equals(e.getStatus()))
// 过滤入职满一年
.filter(e -> e.getHireDate().isBefore(oneYearAgo))
// 按薪资降序排序
.sorted(Comparator.comparing(Employee::getSalary).reversed())
// 取前N名
.limit(topN)
// 收集结果
.collect(Collectors.toList());
}
public Map<String, DoubleSummaryStatistics> getSalaryStatsByDept(List<Employee> employees) {
return employees.stream()
.filter(e -> "ACTIVE".equals(e.getStatus()))
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.summarizingDouble(e -> e.getSalary().doubleValue())
));
}
}Stream使用注意事项
避免重复消费
Stream<String> stream = list.stream().filter(s -> s.length() > 3);
stream.forEach(System.out::println);
stream.count(); // 抛出IllegalStateException注意操作顺序
合理安排操作顺序可以提高性能:先过滤减少数据量,再进行转换或排序。
// 推荐:先过滤再排序
list.stream()
.filter(item -> item.isActive())
.sorted()
.collect(Collectors.toList());
// 不推荐:先排序再过滤
list.stream()
.sorted()
.filter(item -> item.isActive())
.collect(Collectors.toList());正确处理空值
// 使用Optional安全处理可能为空的结果
Optional<User> user = users.stream()
.filter(u -> u.getId().equals(targetId))
.findFirst();
user.ifPresent(u -> System.out.println(u.getName()));
String name = user.map(User::getName).orElse("未知用户");掌握Stream API能够大幅提升集合处理代码的简洁性和可读性,但需要理解其惰性求值机制和一次消费特性,避免常见陷阱。
更新: 2025-12-04 17:36:39
原文: https://www.yuque.com/u22210564/zoxfmt/doc-16-stream-02