Java数组排序: Array-ArrayList-List-Collections.sort()/List.sort()/Arrays.sort()

本文探讨了Java中对ArrayList/List及Array的排序方法,包括Collections.sort()、List.sort()和Arrays.sort()。ArrayList/List的排序内部调用了Timsort算法,而Array的排序使用了双指针快排,时间复杂度为O(n log n)。文章还提到了不同排序算法的选择与内存局部性、缓存效率的关系。


此文首发于我的Jekyll博客:zhang0peter的个人博客


之前写了一篇博客:Java:获取数组中的子数组的多种方法

现在在做LeetCode题目时想要对数组进行排序,于是想到Java中是否存在C++的标准库中的std::sort()。

Java的排序分为2种,一种是ArrayList或者List;另一种则是Array

ArrayList/List 的排序:Collections.sort()/List.sort()

先说一下排序的各种方法的具体使用示例:sort List

代码如下:

        List<String> strings = Arrays.asList("zoo", "foo", "bar", "baz");
        Collections.sort(strings); 
        System.out.println(strings.toString());
        List<String> strings = Arrays.asList("zoo", "foo", "bar", "baz");
        strings.sort(null);
        System.out.println(strings.toString());

Collections.sort()函数本质上是调用List.sort():

    //Collections.sort()
    public static <T extends Comparable<? super T>> void sort(List<T> list) {
        list.sort(null);
    }

对于所有实现了接口List的类,如ArrayList,自然继承了函数sort,而List.sort()本质上调用了Arrays.sort()

    //List.sort()
    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }

Arrays.sort()函数的具体实现如下:

    public static <T> void sort(T[] a, Comparator<? super T> c) {
        if (c == null) {
            sort(a);
        } else {
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, c);
            else
                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        }
    }
    public static void sort(Object[] a) {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a); //旧的排序算法,不推荐
        else
            ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
    }
    private static void legacyMergeSort(Object[] a) {
        Object[] aux = a.clone();
        mergeSort(aux, a, 0, a.length, 0);
    }

至于TimSort这种排序算法的具体实现,参考这篇博客:Java内置排序算法:Timsort详解

如果想自定义Comparator,参考这篇博客:Java 8 Comparator: How to Sort a List - DZone Java

Array 的排序:Arrays.sort()

List中存放的只能是类的对象,或者包装类的对象,如:Long, Integer。

而如果我们把对象直接存放在数组中,也就是 Array 中,如 存放int, long在 Array 数组中,排序的方法如下:

        int[] nums = {-2, 5, -1};
        Arrays.sort(nums);
        System.out.println(Arrays.toString(nums));

可以看出不管是基本类型数组,还是List,最终都是调用Arrays.sort进行最终的排序。

因为函数的对象是基本类型,因此Arrays.sort会直接调用函数进行排序,使用的排序算法是Dual-Pivot Quicksort:

    public static void sort(int[] a) {
        DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
    }

使用的不是普通的快排,而是双指针快排,最坏的时间复杂度为O(n log(n))

看到这里会产生疑问,为什么对List的排序使用算法TimSort,而对数组的排序使用算法Dual-Pivot Quicksort

网上有个讨论:algorithm - Comparison between timsort and quicksort - Stack Overflow

快排适合原始数组是因为内存的局部性和缓存。

里面有个推测说,对象数组存放的仅仅是对象的引用,而实际的比较需要从堆中读取对象,因此TimSort更合适。

Python中的所有数据都是对象,因此Python的内置排序算法也是TimSortsorting - What algorithm does python’s sorted() use? - Stack Overflow

### Arrays.sortCollections.sort 的使用场景与性能对比 #### 1. 数据结构适用性 当需要对 **数组** 进行排序时,`Arrays.sort()` 是更自然的选择。该方法支持基本数据类型的数组以及对象数组排序[^3]。 对于 **集合** 类型(尤其是实现了 `List` 接口的对象),`Collections.sort()` 更加适合。它可以操作任何实现了 `List` 接口的集合,并提供灵活的自定义比较器功能[^4]。 #### 2. 底层实现差异 - 对于 `Arrays.sort()` 方法,在 JDK 1.7 及之后版本中,针对基本数据类型采用的是 **Dual-Pivot Quicksort (双轴快速排序)** 算法,这是一种改进版的快速排序算法,具有更好的平均时间复杂度 O(n log n)[^5]。 - 而 `Collections.sort()` 则基于 **TimSort** 算法实现。TimSort 结合了归并排序和插入排序的优点,特别适用于已部分有序的数据集,其最坏情况下的时间复杂度也是 O(n log n),但在实际应用中通常表现更好[^5]。 #### 3. 性能考量 - 当处理大量基础数据类型(如 int、double 等)时,由于不需要封装成对应的包装类实例(Integer 或 Double),因此 `Arrays.sort()` 在内存占用上会更加高效。 - 针对复杂的对象或者较大的列表而言,虽然两者的时间复杂度相同,但由于 TimSort 更擅长处理接近有序的情况,所以在某些特定条件下可能表现出略微的优势。 以下是两种方法的一个简单示例: ```java // 使用 Arrays.sort() int[] array = {9, 8, 7, 6}; Arrays.sort(array); // 默认升序排列 // 自定义 Comparator 示例 String[] stringsArray = {"banana", "apple", "orange"}; Arrays.sort(stringsArray, new Comparator<String>() { @Override public int compare(String s1, String s2) { return Integer.compare(s1.length(), s2.length()); } }); // 使用 Collections.sort() List<Integer> list = new ArrayList<>(Arrays.asList(9, 8, 7, 6)); Collections.sort(list); // 带有自定义 Comparator 的 List 排序 List<String> stringList = new ArrayList<>(Arrays.asList("banana", "apple", "orange")); Collections.sort(stringList, new Comparator<String>() { @Override public int compare(String o1, String o2) { return Integer.compare(o1.length(), o2.length()); } }); ``` #### 4. 特殊注意事项 尽管两者的底层逻辑存在相似之处,但它们各自优化的方向不同——前者专注于原始数据类型的高速运算;后者则提供了更大的灵活性以适应多样化的业务需求。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值