You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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]]

请问是否存在更简洁、性能更优的实现方式?

优化方案

你当前的写法存在三个明显的性能和逻辑问题:

  1. 调用了两次Collections.sort做排序,且第二次排序的比较器不符合规范:当两个元素的自定义顺序不同时直接返回0,相当于判定二者相等,完全依赖第一次排序的结果和Java排序的稳定性才得到正确结果,换用非稳定排序实现就会出现乱序,两次排序本身也带来了不必要的性能开销
  2. 每次比较都调用definedOrder.indexOf(),这个方法是线性遍历列表查询,时间复杂度O(n),排序比较次数多的时候性能很差
  3. 日期解析逻辑放在比较器内,同一条记录的日期字符串会在多次比较中被重复解析,浪费大量算力;且异常捕获直接打印栈轨迹后返回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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 22:48:26