Collections.sort() in JDK1.6

Posted java-meng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Collections.sort() in JDK1.6相关的知识,希望对你有一定的参考价值。

本文主要介绍了Collections.sort方法在JDK1.6中的源码实现(JDK版本1.6.0_45)

1.Collections.sort()

public static <T> void sort(List<T> list, Comparator<? super T> c) {
        Object[] a = list.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator i = list.listIterator();
        for (int j = 0; j < a.length; j++) {
            i.next();
            i.set(a[j]);
        }
    }

首先这个方法将list转换为数组并调用了Arrays.sort()方法(至于这个Comparator是一种比较器,外界需要实现其compare方法;还有一种是Comparable,实现的是compareTo方法,有兴趣的可以研究一下。),在对数组进行排序之后,通过迭代器将排序结果更新到list中。(迭代器的相关知识不做介绍了,因为我也没看这部分的源码)

2.Arrays.sort()

public static <T> void sort(T[] a, Comparator<? super T> c) {
    T[] aux = (T[])a.clone();
        if (c==null)
            mergeSort(aux, a, 0, a.length, 0);
        else
            mergeSort(aux, a, 0, a.length, 0, c);
    }

先是利用Object的clone方法创建了一个a的副本对象(Object的clone方法为浅克隆方式,它只克隆了自身对象和对象内部实例变量的地址引用。也就是说在堆内存只是创建了a自身副本对象,而在栈内存中创建了对象内部实例变量的地址引用副本。)

判断比较器c存不存在,实际上mergeSort(aux, a, 0, a.length, 0)和mergeSort(aux, a, 0, a.length, 0, c)内部实现方式一样,只是如果比较器c(Comparator)不存在,mergeSort(aux, a, 0, a.length, 0)内部会使用Comparable比较器的compareTo方法。

3.mergeSort(归并排序)

private static void mergeSort(Object[] src, Object[] dest, int low,
            int high, int off, Comparator c) {
        int length = high - low;

        // Insertion sort on smallest arrays
        if (length < INSERTIONSORT_THRESHOLD) {
            for (int i = low; i < high; i++)
                for (int j = i; j > low && c.compare(dest[j - 1], dest[j]) > 0; j--)
                    swap(dest, j, j - 1);
            return;
        }

        // Recursively sort halves of dest into src
        int destLow = low;
        int destHigh = high;
        low += off;
        high += off;
        int mid = (low + high) >>> 1;
        mergeSort(dest, src, low, mid, -off, c);
        mergeSort(dest, src, mid, high, -off, c);

        // If list is already sorted, just copy from src to dest. This is an
        // optimization that results in faster sorts for nearly ordered lists.
        if (c.compare(src[mid - 1], src[mid]) <= 0) {
            System.arraycopy(src, low, dest, destLow, length);
            return;
        }

        // Merge sorted halves (now in src) into dest
        for (int i = destLow, p = low, q = mid; i < destHigh; i++) {
            if (q >= high || p < mid && c.compare(src[p], src[q]) <= 0)
                dest[i] = src[p++];
            else
                dest[i] = src[q++];
        }
    }

这一部分代码比较多,首先程序会通过判断length < INSERTIONSORT_THRESHOLD(INSERTIONSORT_THRESHOLD默认为7),如果数组长度小于7,会使用直接插入排序算法。

这个上张图解释一下,图片来源https://www.cnblogs.com/chengxiao/p/6103002.html

现在来看一下代码,代码很简单,两个循环,外层循环遍历数组(图中的待排序序列),内层循环将每一次待排序的值按顺序放入有序序列中(只是一种表达方式,实际上就是交换),比如图中在对1进行插入时,会先跟前一个元素9进行比较,1<9,交换位置,然后1再跟前一个元素3比较,1<3,交换位置。

技术分享图片

要是数组长度不小于7呢,就会使用归并排序算法啦,有关归并排序算法的介绍可以参考这篇博客https://www.cnblogs.com/chengxiao/p/6194356.html,这里就不贴图了。

归并排序的核心思想呢就是分而治之嘛,说起来挺容易的哈!

下面代码通过采用递归方式实现分的思想,分到最后还是用了直接插入排序,这个有个地方需要注意一下,就是src和dest的参数位置(数组的起止位置就不用说了吧!),在调用递归时,是将src数组作为排序对象进行排序的,然后dest数组是依据src进行排序(下边代码具体说)。

int destLow = low;
int destHigh = high;
low += off;
high += off;
int mid = (low + high) >>> 1;
mergeSort(dest, src, low, mid, -off, c);
mergeSort(dest, src, mid, high, -off, c);

现在分完了,怎么治呢?前面所说的dest数组是依据src进行排序在此进行了体现,最开始说的两个mergeSort方法的不同之处(比较器)在这也能看出。

首先有一种特别友好的情况,那就是分完的前半部分最大的那个正好小于等于后半部分最小的元素(这只是递归过程中的一个分支),那么皆大欢喜,src数组已经是有序的了,只需将src数组复制到目标数组中(System.arraycopy具体用法自行上网查吧)。

// If list is already sorted, just copy from src to dest. This is an
// optimization that results in faster sorts for nearly ordered lists.
if (c.compare(src[mid - 1], src[mid]) <= 0) {
    System.arraycopy(src, low, dest, destLow, length);
    return;
}

// Merge sorted halves (now in src) into dest
for (int i = destLow, p = low, q = mid; i < destHigh; i++) {
     if (q >= high || p < mid && c.compare(src[p], src[q]) <= 0)
           dest[i] = src[p++];
     else
           dest[i] = src[q++];
}

其他情况怎么处理呢,这个再贴张图,图片来源https://blog.csdn.net/ghsau/article/details/42060651,这篇博客是我参考的博客,大家也可以看看。

思想很简单,循环整个数组,比较左边的元素和右边的元素,谁小把谁放到dest中(再次重申,这只是递归过程中的一个分支),然后移动对应指针,直到src所有元素都放入到dest中。

例如一开始是2和1进行比较,1小放入dest中,p不变,q指向5(看做指针),i指向下一个位置;2和5比较,2小放入dest中,p指向3,q不变,i指向下一个位置,以此类推,直到循环结束。

技术分享图片

到这里算是写完了,写的可能不好,刚毕业大学生水平有限,工作之后的第一篇博客,请原谅我一些地方的无知;如果有什么错误,请一定要指出,万分感谢,您的意见是我成长的催化剂。

下面整理一下参考资料。

https://blog.csdn.net/ghsau/article/details/42060651

https://www.jianshu.com/p/1efc3aa1507b

https://www.cnblogs.com/chengxiao/p/6103002.html

https://www.cnblogs.com/chengxiao/p/6194356.html

 后面如果有机会,会写一篇jdk1.7之后有关sort这一部分的博客。

疑问:mergeSort中有一个off参数,个人理解是限制排序集合的区域范围(起止怎么确定的。。。)?JDK1.7中sort方法中的paramInt1和paramInt2是不是起到了类似的功能?

以上是关于Collections.sort() in JDK1.6的主要内容,如果未能解决你的问题,请参考以下文章

jdk8 Collections#sort究竟做了什么

jdk8 Collections#sort究竟做了什么

为啥 Collections.sort 使用合并排序而不是快速排序?

为啥 Collections.sort 使用 Mergesort 而 Arrays.sort 不使用?

当比较器返回0时,java 8中的Collections.sort不能用作java 6

jdk8源码TimSort算法——从头看到脚