Java的排序源码分析

最近刷算法题经常用到Arrays.sort(), Collections.sort()等方法。相信写java的大佬们对排序算法有很好的实现,所以特地点开源码学习学习。

本文代码分析基于java1.8

Collections.sort()

点开Collections.sort() -> list.sort(null):

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

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);
    }
}

可以发现,对List类的排序是通过调用Arrays.sort()实现的。

Arrays.sort()

点开Arrays类的结构,可以看到有大量重载的sort方法,其大致可基于入参分为两类:除了boolean的基本数据类型,非基本数据类型(泛型T)。

下面对二者分别进行分析

基本数据类型

以int为例:

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

根据该方法注释:

This algorithm offers O(n log(n)) performance on many data sets that cause other
quicksorts to degrade to quadratic performance, and is typically faster than traditional (one-pivot) Quicksort implementations.

DualPivotQuicksort为某种比普通快排更优秀的快排,其具体实现重点代码有:

static void sort(int[] a, int left, int right,
                     int[] work, int workBase, int workLen) {
        // Use Quicksort on small arrays
        if (right - left < QUICKSORT_THRESHOLD) {
            sort(a, left, right, true);
            return;
        }
		//...

        // Check if the array is nearly sorted
		//...

            /*
             * The array is not highly structured,
             * use Quicksort instead of merge sort.
             */
            if (++count == MAX_RUN_COUNT) {
                sort(a, left, right, true);
                return;
            }
        }

        // Merging
		//...
        }
    }

结合常量注释分析可得:

  1. QUICKSORT_THRESHOLD = 286为是否使用快排的阈值。
    当数组长度小于该阈值时,使用"快排",否则进入下一步判断
  2. MAX_RUN_LENGTH = 67为判断数组是否已有一定有序性的阈值。
    当有序性大于该阈值时,使用"快排",否则进入归并排序

点开"快排"方法sort(a, left, right, true)可发现,事情还没那么简单:

private static void sort(int[] a, int left, int right, boolean leftmost) {
    // Use insertion sort on tiny arrays
    if (length < INSERTION_SORT_THRESHOLD) {
        //insertion sort
    }

    //quick sort
    }
}

没错!还有一层:
INSERTION_SORT_THRESHOLD = 47为是否使用插入排序的阈值
当数组长度小于该阈值时,使用插入排序,否则进行快排

总结

请添加图片描述

为什么要三种排序方法混用

  1. 对于小数组来说,插入排序性能更高,时间复杂度相差不大
  2. 对于大数组,若数组无序组够多,使用快速排序(快排数据越无序越快)
  3. 若数组太大且有一定有序性,使用稳定的归并排序

非基本数据类型

对于非基本数据类型,如非特别要求,使用TimSort算法

public static void sort(Object[] a) {
    if (LegacyMergeSort.userRequested)
        legacyMergeSort(a);
    else
        ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}

Timsort是一种结合了归并排序和插入排序的混合排序算法。可视为一种归并排序,此处不做展开。

为什么对非基本数据类型不用快排而用归并

重点在于 快排不是稳定算法,而归并排序是!

  • 对于基本数组类型,稳定性没有太大意义,所以可以使用不稳定的快排,但是对于对象类型,稳定性是比较重要的,对象相等的复杂性让我们没办法保证每个人都会重写正确的equal方法,故使用稳定算法的归并排序和插入排序结合的TimSort算法。

  • 对于归并排序来说,比较次数比快速排序少,移动次数比快排多,而对于对象来说,比较是相对耗时的操作,所以并不适合快排,对于基本数据类型来说,比较和移动都不怎么耗时,所以用归并和快速都可以。

参考

  1. Java排序算法Sort源码分析
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值