发布日期:2024年1月 | 作者:Java技术专家
一、Stream API概述
Java 8引入的Stream API彻底改变了集合处理的方式,它提供了一种声明式、函数式的数据处理方法。与传统的迭代式编程相比,Stream API让代码更加简洁、易读,并且能够充分利用多核架构的优势。
Stream不是数据结构,它更像是高级的Iterator,可以对数据源进行各种复杂的流水线操作,最终得到我们需要的结果。这种编程范式让开发者能够更专注于”做什么”而不是”怎么做”。
二、核心概念与特性
1. 流的三个特征
- 无存储:流不存储元素,只是从数据源传递元素
- 函数式操作:操作不会修改源数据
- 延迟执行:中间操作都是延迟的,只有终端操作才会执行
2. 流与集合的区别
集合关注的是数据的存储,而流关注的是数据的计算。流就像是数据的”流水线”,数据在管道中传输,经过各种处理操作。
3. 流的操作类型
// 中间操作 - 返回新的Stream
Stream<T> filter(Predicate<? super T> predicate)
Stream<T> sorted()
Stream<T> distinct()
// 终端操作 - 产生结果或副作用
void forEach(Consumer<? super T> action)
long count()
Optional<T> min(Comparator<? super T> comparator)
三、基础操作详解
1. 流的创建
// 从集合创建
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream1 = list.stream();
// 从数组创建
String[] array = {"a", "b", "c"};
Stream<String> stream2 = Arrays.stream(array);
// 使用Stream.of
Stream<String> stream3 = Stream.of("a", "b", "c");
// 生成无限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2);
2. 常用中间操作
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 过滤操作
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// 映射操作
List<Integer> squares = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
// 排序操作
List<Integer> sortedNumbers = numbers.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
3. 常用终端操作
// 收集操作
List<String> names = Arrays.asList("John", "Jane", "Jack");
Set<String> nameSet = names.stream()
.collect(Collectors.toSet());
// 聚合操作
Optional<Integer> max = numbers.stream()
.max(Integer::compareTo);
// 匹配操作
boolean anyMatch = numbers.stream()
.anyMatch(n -> n > 5);
四、高级应用模式
1. 分组与分区
class Product {
private String category;
private String name;
private double price;
// 构造方法、getter、setter省略
}
List<Product> products = Arrays.asList(
new Product("电子", "手机", 2999.0),
new Product("电子", "电脑", 5999.0),
new Product("图书", "Java编程", 89.0)
);
// 按类别分组
Map<String, List<Product>> productsByCategory = products.stream()
.collect(Collectors.groupingBy(Product::getCategory));
// 价格分区(高于1000和低于1000)
Map<Boolean, List<Product>> expensiveProducts = products.stream()
.collect(Collectors.partitioningBy(p -> p.getPrice() > 1000));
2. 并行流处理
List<Integer> largeList = // 大量数据
// 顺序处理
long startTime = System.currentTimeMillis();
List<Integer> result1 = largeList.stream()
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.collect(Collectors.toList());
long sequentialTime = System.currentTimeMillis() - startTime;
// 并行处理
startTime = System.currentTimeMillis();
List<Integer> result2 = largeList.parallelStream()
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.collect(Collectors.toList());
long parallelTime = System.currentTimeMillis() - startTime;
五、性能优化技巧
1. 避免装箱拆箱
// 不好的做法 - 涉及装箱拆箱
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.mapToInt(Integer::intValue) // 避免这个转换
.sum();
// 好的做法 - 使用原始类型流
IntStream intStream = IntStream.range(1, 6);
int optimizedSum = intStream.sum();
2. 短路操作优化
List<String> names = Arrays.asList("John", "Jane", "Jack", "Jill");
// findFirst会在找到第一个匹配项后立即停止处理
Optional<String> result = names.stream()
.filter(name -> name.startsWith("J"))
.findFirst();
六、实战案例:电商订单处理系统
业务场景
我们需要处理一个电商平台的订单数据,实现以下功能:
- 统计每个用户的订单总金额
- 找出消费金额最高的前3名用户
- 按商品类别统计销售额
- 筛选出待处理的异常订单
领域模型
class Order {
private Long orderId;
private Long userId;
private List<OrderItem> items;
private OrderStatus status;
private LocalDateTime createTime;
// 构造方法、getter、setter
public double getTotalAmount() {
return items.stream()
.mapToDouble(OrderItem::getAmount)
.sum();
}
}
class OrderItem {
private Long productId;
private String productName;
private String category;
private Integer quantity;
private Double price;
public double getAmount() {
return quantity * price;
}
}
enum OrderStatus {
PENDING, PAID, SHIPPED, COMPLETED, CANCELLED
}
Stream API实现
public class OrderProcessor {
private List<Order> orders;
public OrderProcessor(List<Order> orders) {
this.orders = orders;
}
// 统计每个用户的订单总金额
public Map<Long, Double> getUserTotalAmount() {
return orders.stream()
.collect(Collectors.groupingBy(
Order::getUserId,
Collectors.summingDouble(Order::getTotalAmount)
));
}
// 找出消费金额最高的前3名用户
public List<Map.Entry<Long, Double>> getTop3Users() {
return getUserTotalAmount().entrySet().stream()
.sorted(Map.Entry.<Long, Double>comparingByValue().reversed())
.limit(3)
.collect(Collectors.toList());
}
// 按商品类别统计销售额
public Map<String, Double> getSalesByCategory() {
return orders.stream()
.flatMap(order -> order.getItems().stream())
.collect(Collectors.groupingBy(
OrderItem::getCategory,
Collectors.summingDouble(OrderItem::getAmount)
));
}
// 筛选异常订单(创建时间超过24小时未支付的订单)
public List<Order> getAbnormalOrders() {
LocalDateTime twentyFourHoursAgo = LocalDateTime.now().minusHours(24);
return orders.stream()
.filter(order -> order.getStatus() == OrderStatus.PENDING)
.filter(order -> order.getCreateTime().isBefore(twentyFourHoursAgo))
.collect(Collectors.toList());
}
// 并行处理大量订单数据
public Map<OrderStatus, Long> getOrderCountByStatusParallel() {
return orders.parallelStream()
.collect(Collectors.groupingByConcurrent(
Order::getStatus,
Collectors.counting()
));
}
}
测试用例
public class OrderProcessorTest {
@Test
public void testOrderProcessing() {
// 准备测试数据
List<Order> orders = createTestOrders();
OrderProcessor processor = new OrderProcessor(orders);
// 测试用户消费统计
Map<Long, Double> userAmounts = processor.getUserTotalAmount();
assertFalse(userAmounts.isEmpty());
// 测试Top3用户
List<Map.Entry<Long, Double>> topUsers = processor.getTop3Users();
assertEquals(3, topUsers.size());
// 测试分类统计
Map<String, Double> categorySales = processor.getSalesByCategory();
assertTrue(categorySales.containsKey("电子"));
}
private List<Order> createTestOrders() {
// 创建测试订单数据
return Arrays.asList(
// 具体的订单实例
);
}
}
七、总结与最佳实践
通过本教程的深入学习,我们掌握了Java Stream API的核心概念和实际应用:
- Stream API的声明式编程优势
- 各种中间操作和终端操作的组合使用
- 并行流处理的适用场景和注意事项
- 在实际项目中的综合应用模式
最佳实践建议:
- 优先使用Stream API处理集合数据,提高代码可读性
- 注意流的延迟执行特性,避免重复创建流
- 在适当场景使用并行流,但要注意线程安全问题
- 合理使用原始类型特化流避免装箱拆箱开销
- 结合Optional处理可能为空的结果
Stream API是现代Java开发中不可或缺的重要工具,熟练掌握它将显著提升你的编程效率和代码质量。