跳到主要内容

Stream API核心操作详解

Stream概述

Stream API是Java 8引入的用于处理集合数据的高级抽象。它提供了一种声明式的数据处理方式,类似于SQL语句对数据库的查询操作,让开发者能够专注于"做什么"而非"怎么做"。

Stream可以被理解为数据管道:数据从源头流入,经过一系列中间处理节点(过滤、转换、排序等),最终产生结果输出。

Stream的核心特性

非存储性

Stream本身不存储数据,它只是数据源的一个视图。数据源可以是集合、数组、I/O通道或生成器函数。

函数式风格

对Stream的任何操作都不会改变原始数据源。例如,过滤操作不会删除原集合中的元素,而是生成一个不包含被过滤元素的新Stream。

惰性求值

Stream的中间操作是延迟执行的,只有遇到终端操作时才会真正执行所有操作。这种机制可以实现高效的短路求值。

一次性消费

Stream只能被遍历一次,遍历完成后Stream即失效。如需再次操作,必须从数据源重新创建Stream。

创建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,支持链式调用。

filter - 条件过滤

根据指定条件筛选元素:

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());

sorted - 排序

支持自然排序和自定义排序:

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());

终端操作详解

终端操作会触发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();

完整处理流程示例

public 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能够大幅提升集合处理代码的简洁性和可读性,但需要理解其惰性求值机制和一次消费特性,避免常见陷阱。