掌握Java-Array/List排序
说Array/List排序之前先说两个和比较相关的重要接口:Comparable
和Comparator
。
Comparable接口
1 | public interface Comparable<T> { |
Comparable接口表示实现它的类是可以被排序的。这种排序被称为类的natural ordering。
本对象小于比较对象,compareTo返回负数。本对象大于比较对象,compareTo返回正数。本对象等于比较对象,compareTo返回0。
大部分情况下e1.compareTo(e2) == 0
与 e1.equals(e2)
应该有相同的结果。
Comparator接口
1 | public interface Comparator<T> { |
Comparable要求被比较的类实现它。而对于那些我们无法修改的类,就无法使用Comparable接口了。Comparator接口适用的就是这种场景。他不需要侵入被比较的类,而是可以作为一个外置的比较器。
compare函数返回的值和compareTo的规则一样。
排序方法
实现了Comparable接口的类的列表或者数组,可以使用Collections.sort或者Arrays.sort来排序。Collections.sort和Arrays.sort还有一个重载的版本,可以通过制定Comparator比较器来排序一般的对象列表或数组。
方法一:使用Comparable接口
1 | // 实现了Comparable接口 |
方法二:使用Comparator接口
1 | static class A{ |
方法三:使用List接口中的sort方法
观察Collections的sort方法定义:
1 | public static <T extends Comparable<? super T>> void sort(List<T> list) { |
发现其实只是简单的调用了list中的sort方法。对于没有指定比较器的情况,是往list的sort方法中传入null。所以我们也可以直接调用List的sort方法。
不过使用Collections中的方法会是更好的选择,因为其函数对List是否可以自然排序做出了要求。如果你对一个没有实现自然排序的对象的列表使用Collections.sort是会在编译的时候报错的。
来看看List.sort是如何定义的:
1 | default void sort(Comparator<? super E> c) { |
看到default可以得知,这是在1.8中更新的代码。查看1.7的JDK代码,1.7中的Collections.sort代码如下:
1 | public static <T extends Comparable<? super T>> void More ...sort(List<T> list) { |
可以得知,1.7中List接口中是没有sort方法的,只能通过Collections的sort方法来排序,而1.8中,List接口添加了sort这个默认方法,Collections的sort也改为调用List中的sort方法了。
排序的原理
无论是1.8还是1.8之前,排序的核心代码都是:
1 | Object[] a = this.toArray(); |
步骤如下:
- 首先列表调用了toArray方法,把自己转换成一个数组
- 然后调用数组的排序Arrays.sort,排序数组
- 最后再根据排序好的数组,利用迭代器,一个一个更新列表里的元素。
撇开Arrays.sort里的算法不说,因为toArray是会复制一份数据的,所以这里的排序不是在原来的底层存储上排序的,而是在拷贝上排序,然后在更新回去。
然后看看Arrays.sort的源码:
1 | public static <T> void sort(T[] a, Comparator<? super T> c) { |
Java1.7之前数组排序使用的是MergeSort,而1.7中升级为TimSort。但是Java1.7也允许使用MergeSort,只要添加参数-Djava.util.Arrays.useLegacyMergeSort=true
即可。
关于MergeSort和TimSort算法的原理,下篇见分解。
总结
- 实现Comparable接口,使类可以被自然排序
- 可以使用实现了Comparator的比较器比较一个类
- 排序的入口方法为Collections.sort和Arrays.sort
- Collections.sort函数1.8中改为调用List的sort方法
- Collections.sort本质上是先把集合转为数组,再使用Arrays.sort排序,再更新集合
- Arrays.sort使用的算法是TimSort和MergeSort