题目
使用Stream API处理多层嵌套集合并避免空指针
信息
- 类型:问答
- 难度:⭐⭐
考点
Stream API,Optional处理,空指针安全,方法引用,Lambda表达式
快速回答
核心解决方案:
- 使用
flatMap展开嵌套集合 - 用
Optional.ofNullable()包装可能为null的集合 - 通过
mapToDouble和sum计算总和 - 使用
DecimalFormat格式化结果 - 结合方法引用(
::)简化代码
问题场景
在实际业务中常需处理多层嵌套数据结构(如订单系统),需避免空指针异常并高效计算。给定以下类结构:
class Order {
private List<OrderItem> items; // 可能为null
// getter省略
}
class OrderItem {
private Double price; // 可能为null
private Integer quantity; // 可能为null
// getters省略
}要求计算所有订单中有效订单项的总金额(price*quantity),返回保留两位小数的字符串,无订单时返回"0.00"。
解决方案与代码
public String calculateTotal(List<Order> orders) {
double total = Optional.ofNullable(orders)
.orElse(Collections.emptyList())
.stream()
.flatMap(order -> Optional.ofNullable(order.getItems())
.orElse(Collections.emptyList()).stream())
.filter(item -> item.getPrice() != null && item.getQuantity() != null)
.mapToDouble(item -> item.getPrice() * item.getQuantity())
.sum();
return new DecimalFormat("0.00").format(total);
}核心原理
- 空指针防护:
Optional.ofNullable()包装可能为null的集合,结合orElse()返回空集合 - 嵌套展开:
flatMap将每个Order的items展开为单一Stream - 过滤无效数据:
filter移除price或quantity为null的项 - 数值计算:
mapToDouble转换为原始类型流避免装箱开销,sum()高效求和
最佳实践
- 优先使用
orElse(emptyList)而非orElseGet(ArrayList::new)减少对象创建 - 用
mapToDouble替代map+reduce,性能更优 - DecimalFormat线程安全,适合金额格式化
- 在
filter中显式检查null,防御性编程
常见错误
- 直接操作null集合:
orders.stream()在orders为null时抛出NPE - 忽略嵌套null:未处理
order.getItems()为null的情况 - 使用并行流不当:小数据量用
parallelStream()可能降低性能 - 多次遍历Stream:重复操作同一Stream会导致
IllegalStateException
扩展知识
- Java 9增强:
Optional.stream()可进一步简化为:.flatMap(order -> Optional.ofNullable(order.getItems()).stream()) - 替代方案:使用
Objects.requireNonNullElse()(Java 9+)替代Optional.orElse() - 性能考量:超大数据集考虑使用
Collectors.summingDouble()并行收集 - 金额精度:生产环境建议用
BigDecimal替代double避免精度丢失