同事接手一个老项目,里面排序全是Collections.sort,一跑大数据量的列表就卡。翻代码才发现他每加一条就排一次,一次请求排了八百遍。ArrayList排序本身不慢,问题是思路用错了地方。
基本类型直接用 Collections.sort
存Integer、String这类有自然顺序的,Collections.sort(list)就行,底层走TimSort,时间复杂度O(n log n)。想倒序再调Collections.reverse。注意它改的是原列表,返回void,别写成list = Collections.sort(list),编译不过。
对象排序要写 Comparator
存自定义对象时,要么让类实现Comparable接口重写compareTo,要么传一个Comparator。JDK 8以后用lambda最简洁:list.sort(Comparator.comparing(User::getAge))。要按年龄降序再接.reversed(),多字段用.thenComparing()串起来。
users.sort(Comparator.comparing(User::getAge).thenComparing(User::getName).reversed());字段为 null 会直接炸
年龄字段可能为空时,comparing会抛NullPointerException。用Comparator.nullsFirst(Comparator.naturalOrder())包一层,把null排到最前或最后。字符串比较默认区分大小写,要忽略就加String.CASE_INSENSITIVE_ORDER。中文按拼音排,得用Collator.getInstance(Locale.CHINA)。
排序会不会改动原列表
List.sort和Collections.sort都是原地排序,会打乱原来的顺序,需要保留原序就先new ArrayList(list)拷一份。Arrays.asList返回的定长列表不能排序,会抛UnsupportedOperationException,转成ArrayList再操作。多线程环境下别用普通ArrayList,换CopyOnWriteArrayList或者自己在外面加锁。
- 单字段:Comparator.comparing加方法引用,一行搞定。
- 多字段:thenComparing按优先级往下串。
- 可能为空:nullsFirst或nullsLast包一层,别省。