Java按自定义顺序与日期双条件排序嵌套List的优化方案
问题描述
我有一个名为outputToStore的嵌套列表(nested list),具体内容如下:
[[Final, 331, M, 22/03/2020 00:00:00], [Initial, 335, M, 22/06/2022 00:00:00], [Exception, 335, M, 22/05/2022 00:00:00], [Final, 335, M, 20/06/2022 00:00:00], [Keep, 335, M, 02/06/2022 11:00:00], [Final, 335, M, 10/04/2022 02:00:00], [Deleted, 335, M, 22/06/2022 15:55:10], [Exception, 335, M, 22/06/2022 15:55:09], [Final, 335, M, 22/06/2022 15:56:00], [Initial, 335, M, 11/06/2022 00:00:00]]
我需要基于两个条件对该列表排序:
- 第一优先级:自定义顺序
"Initial","Final","Deleted","Keep","Exception" - 第二优先级:列表中的
datetime字段
我目前已经实现了排序效果,但不确定当前写法是不是最优方案。
我的实现代码:
List<String> definedOrder = Arrays.asList("Initial","Final","Deleted","Keep","Exception"); Collections.sort(outputToStore, Comparator.comparing(o -> Integer.valueOf(definedOrder.indexOf(o.get(0))))); Collections.sort(outputToStore,( o1, o2)-> { // 按照自定义顺序匹配排序优先级 try { if(Integer.valueOf(definedOrder.indexOf(o1.get(0))).compareTo(Integer.valueOf(definedOrder.indexOf(o2.get(0))))==0){ Date date1=simpleDateFormat.parse(o1.get(3)); Date date2=simpleDateFormat.parse(o2.get(3)); return date1.compareTo(date2); } } catch (ParseException e) { e.printStackTrace(); } return 0; });
当前运行可以得到预期结果:
[[Initial, 335, M, 11/06/2022 00:00:00], [Initial, 335, M, 22/06/2022 00:00:00], [Final, 331, M, 22/03/2020 00:00:00], [Final, 335, M, 10/04/2022 02:00:00], [Final, 335, M, 20/06/2022 00:00:00], [Final, 335, M, 22/06/2022 15:56:00], [Deleted, 335, M, 22/06/2022 15:55:10], [Keep, 335, M, 02/06/2022 11:00:00], [Exception, 335, M, 22/05/2022 00:00:00], [Exception, 335, M, 22/06/2022 15:55:09]]
请问是否存在更简洁、性能更优的实现方式?
优化方案
你当前的写法存在三个明显的性能和逻辑问题:
- 调用了两次
Collections.sort做排序,且第二次排序的比较器不符合规范:当两个元素的自定义顺序不同时直接返回0,相当于判定二者相等,完全依赖第一次排序的结果和Java排序的稳定性才得到正确结果,换用非稳定排序实现就会出现乱序,两次排序本身也带来了不必要的性能开销 - 每次比较都调用
definedOrder.indexOf(),这个方法是线性遍历列表查询,时间复杂度O(n),排序比较次数多的时候性能很差 - 日期解析逻辑放在比较器内,同一条记录的日期字符串会在多次比较中被重复解析,浪费大量算力;且异常捕获直接打印栈轨迹后返回0,既会拖慢性能,也可能在解析失败时导致排序结果混乱
高性能实现代码
核心思路是把高频重复调用的逻辑提前预计算,用O(1)的哈希查询替代线性遍历,用多条件比较器单次完成排序:
// 1. 自定义顺序用HashMap存储,查询时间复杂度O(1),替代线性查找的List.indexOf Map<String, Integer> orderMap = new HashMap<>(); orderMap.put("Initial", 0); orderMap.put("Final", 1); orderMap.put("Deleted", 2); orderMap.put("Keep", 3); orderMap.put("Exception", 4); // 2. 提前预解析所有日期,避免比较时重复解析,提前处理解析异常 SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); List<SortKey> sortKeys = new ArrayList<>(); for (List<String> item : outputToStore) { try { Date date = sdf.parse(item.get(3)); sortKeys.add(new SortKey(orderMap.get(item.get(0)), date, item)); } catch (ParseException e) { throw new IllegalArgumentException("日期格式非法: " + item.get(3), e); } } // 3. 单次排序,先按自定义顺序比,再按日期比 Collections.sort(sortKeys, Comparator .comparingInt((SortKey k) -> k.order) .thenComparing(k -> k.date) ); // 4. 回写排序后的结果到原列表 outputToStore.clear(); for (SortKey key : sortKeys) { outputToStore.add(key.item); } // 内部类存储预计算的排序key private static class SortKey { final int order; final Date date; final List<String> item; SortKey(int order, Date date, List<String> item) { this.order = order; this.date = date; this.item = item; } }
如果不想写额外内部类,也可以用流式写法实现,性能接近上述方案,代码更简洁:
Map<String, Integer> orderMap = Map.of( "Initial", 0, "Final", 1, "Deleted", 2, "Keep", 3, "Exception", 4 ); SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); List<List<String>> sorted = outputToStore.stream() .sorted(Comparator .comparingInt((List<String> item) -> orderMap.get(item.get(0))) .thenComparing(item -> { try { return sdf.parse(item.get(3)); } catch (ParseException e) { throw new RuntimeException(e); } }) ) .collect(Collectors.toList()); outputToStore.clear(); outputToStore.addAll(sorted);
注意:
SimpleDateFormat是线程不安全类,如果是多线程场景请替换为Java 8+提供的DateTimeFormatter,对应日期类型使用LocalDateTime即可。
优化收益
- 自定义顺序查询从O(n)线性遍历改成O(1)哈希查询,比较阶段的查询开销大幅降低
- 日期从每次比较重复解析,改成每个元素仅解析1次,消除了重复解析的性能浪费
- 仅需一次排序即可完成逻辑,比较器实现符合规范,不依赖排序稳定性就能得到正确结果
- 异常提前处理,避免比较过程中出现异常打乱排序结果
内容的提问来源于stack exchange,提问作者sandhu
相关产品推荐
相关产品推荐

