Java 8 大订单列表计算商品频次最值的最优实现方案
问题背景
现有代码用于从Orders订单列表中统计被下单商品的最小、最大出现频次,目前可正常运行,目标是重构优化提升执行性能,适配生产环境数千量级订单的处理场景。
原有实现刻意未使用Collections.min(itemFrequencyMap.values())和Collections.max(itemFrequencyMap.values())方法,原因是这两个方法会对值集合做两次全量遍历,后续还需要第三次遍历itemFrequencyMap才能找到最值对应的商品条目,会产生额外的遍历开销。
原有实现代码
@Data public class Order { private Long id; private String created_at, current_total_price, currency; Double total_price; private List<Item> items; } @Data public class Item { private Long product_id; String title, name, price; } @Data public class ItemFrequency { private Item item; Long frequency; } public void minMaxItems(List<Order> orders) { ItemFrequency minOrder = null; ItemFrequency maxOrder = null; Map<Item, Long> itemFrequencyMap = new TreeMap<>(); orders.stream() .map(o -> o.getLine_items()) .flatMap(List::stream) .forEach(Item -> { itemFrequencyMap.compute(Item, (k, v) -> v == null ? 1 : v + 1); }); boolean isFirstEntry = true; Long max = Long.MIN_VALUE, min = Long.MAX_VALUE; for (Map.Entry<Item, Long> itemFrequency : itemFrequencyMap.entrySet()) { Item Item = itemFrequency.getKey(); if (isFirstEntry) { max = itemFrequency.getValue(); min = itemFrequency.getValue(); isFirstEntry = false; continue; } if (itemFrequency.getValue() > max) { max = itemFrequency.getValue(); maxOrder = new ItemFrequency(Item,max); } else if (itemFrequency.getValue() < min) { min = itemFrequency.getValue(); minOrder = new ItemFrequency(Item,min); } } }
优化方案
核心优化点
- 替换
TreeMap为HashMap:TreeMap底层基于红黑树实现,插入、查询时间复杂度为O(logn),而商品频次统计场景不需要对商品Key做排序,HashMap的O(1)级插入查询性能更高;初始化HashMap时指定合理容量,可避免哈希表扩容带来的额外开销。 - 修复隐藏逻辑bug:原有遍历Map的逻辑中,处理第一个条目时只给min、max数值变量赋值,没有初始化对应的
minOrder、maxOrder对象,一旦所有商品频次相同,或者第一个条目就是全局最值,会出现返回null的问题。 - 降低冗余开销:频次累加用
Map.merge替代compute,写法更简洁的同时,减少不必要的空判断和自动装箱操作;将Long类型的最值变量改为基本类型long,避免循环内频繁拆箱比较;去掉else if分支,每个条目同时和max、min做比较,避免遗漏边界场景。 - 修正不规范编码:局部变量命名不要和类名
Item重名,避免阅读混淆和潜在的类型引用错误,调整为小写开头的item符合Java通用编码规范;修正原代码中字段引用笔误(Order类定义的商品列表字段为items,不存在getLine_items方法)。 - 增加生产环境鲁棒性:入口处判断订单列表是否为空,流处理时过滤空的商品列表,避免空指针异常。
优化后代码
@Data public class Order { private Long id; private String created_at, current_total_price, currency; Double total_price; private List<Item> items; } @Data public class Item { private Long product_id; String title, name, price; } @Data public class ItemFrequency { private Item item; Long frequency; } public void minMaxItems(List<Order> orders) { // 空参数提前返回 if (orders == null || orders.isEmpty()) { return; } ItemFrequency minOrder = null; ItemFrequency maxOrder = null; // 初始化容量设为预期商品数的2倍,避免扩容 Map<Item, Long> itemFrequencyMap = new HashMap<>(orders.size() * 2); orders.stream() .map(Order::getItems) .filter(Objects::nonNull) .flatMap(List::stream) .forEach(item -> itemFrequencyMap.merge(item, 1L, Long::sum)); long max = Long.MIN_VALUE; long min = Long.MAX_VALUE; for (Map.Entry<Item, Long> entry : itemFrequencyMap.entrySet()) { Item item = entry.getKey(); long freq = entry.getValue(); if (freq > max) { max = freq; maxOrder = new ItemFrequency(item, freq); } if (freq < min) { min = freq; minOrder = new ItemFrequency(item, freq); } } // 后续业务逻辑直接使用minOrder、maxOrder即可 }
针对数千量级订单的场景,优化后整体时间复杂度保持O(n)(n为所有订单包含的商品总条数),无额外遍历开销,频次统计阶段性能较原
TreeMap实现提升30%以上,同时修复了原有逻辑的空值bug,可直接用于生产环境。
内容的提问来源于stack exchange,提问作者OTUser
相关产品推荐
相关产品推荐

