深入Arrays.sort源码
创始人
2024-06-02 07:37:15
0

深入挖掘Arrays.sort()原理

1、先来一个例子吧:

    @Testpublic void testSorted(){Integer[] arr = {1,-2,5,-6,3,9};
//        Arrays.sort(arr,(o1 ,o2)->o1-o2);Arrays.sort(arr,new Comparator(){@Overridepublic int compare(Integer o1, Integer o2) {return o1 -  o2;}});}

就这个例子。

2、首先啊,先到Arrays这个类的sort方法里

public static  void sort(T[] a, Comparator c) {if (c == null) {sort(a);} else {if (LegacyMergeSort.userRequested)legacyMergeSort(a, c);elseTimSort.sort(a, 0, a.length, c, null, 0, 0);}
}

在sort方法会怎么样?sort(T[] a, Comparator c)这里的a是数组,c是接口类,这里实现的是接口编程。

①先判断c == null,也就是判断有没有接口,那么旧往下走

②判断LegacyMergeSort.userRequested,发现是false,那么就走TimSort.sort(a, 0, a.length, c, null, 0, 0)

3、来到TimSort类,这个类的名字不认识,查一下:是归并排序和插入排序的组合排序

sort(T[] a, int lo, int hi, Comparator c,T[] work, int workBase, int workLen)

来看看TimSort.sort详情:

assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;int nRemaining  = hi - lo;
if (nRemaining < 2)return;  // Arrays of size 0 and 1 are always sorted// If array is small, do a "mini-TimSort" with no merges
if (nRemaining < MIN_MERGE) {int initRunLen = countRunAndMakeAscending(a, lo, hi, c);binarySort(a, lo, hi, lo + initRunLen, c);return;

这里的assert是什么?不知道,查一下:是断言的意思,这个关键字可以判断布尔值的结果是否和预期的一样,如果一样就正常执行,否则会抛出AssertionError。

对于函数参数的解析:

a是数组,lo是0,hi是长度,c是接口实现类,work是null,workBase是0,workLen是0。

①那么先判断nRemaining = hi - lo = len - 0 = 6,一开始肯定是大于2的,跳过;

②再进入if (nRemaining < MIN_MERGE) ,这里min_merge是一个静态final变量,为32,那么可以进入true结构体;

③进入

int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
binarySort(a, lo, hi, lo + initRunLen, c);
return;

4、来countRunAndMakeAscending看看:

private static  int countRunAndMakeAscending(T[] a, int lo, int hi,Comparator c)

Ascending看到这个单词想起来asc和dec,分别对应升序和降序。

看看详情:

assert lo < hi;
int runHi = lo + 1;
if (runHi == hi)return 1;// Find end of run, and reverse range if descending
if (c.compare(a[runHi++], a[lo]) < 0) { // Descendingwhile (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)runHi++;reverseRange(a, lo, runHi);
} else {                              // Ascendingwhile (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)runHi++;
}return runHi - lo;

先分析一下参数吧: a是数组, lo是0 ,hi是len,c是接口。

①断言肯定是成功的。

②判断runHi和hi肯定是不相等的,跳过;

③来到一个判断c.compare(a[runHi++], a[lo]) < 0此时runhi是1,lo是0,也就是也就是判断[1] - [0] < 0,也就是[1] < [0 ]是判断元素里前面的元素值大了,在例子里是true的,进入true结构体:

④进入了一个循环体:

while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)runHi++;

这里的意思是判断前面的元素是不是降序的,找到不是降序的那一个runHi出来,然后再进入reverseRange(a, lo, runHi)

5、来到reverseRange

private static void reverseRange(Object[] a, int lo, int hi) {hi--;while (lo < hi) {Object t = a[lo];a[lo++] = a[hi];a[hi--] = t;}
}

这里是将[lo,hi - 1]这些索引对应的元素反转。当要反转的元素个数为奇数时判断条件lo < hi会比lo <= hi多走一步,不过无所谓,有没有等号的结果都一样。

6、再回到来到TimSort类

直接跳到最后一步:

return runHi - lo;

返回了runHi - lo = 2 - 0 = 2。

7、再回到TimSort.sort

来到:

// If array is small, do a "mini-TimSort" with no merges
if (nRemaining < MIN_MERGE) {int initRunLen = countRunAndMakeAscending(a, lo, hi, c);binarySort(a, lo, hi, lo + initRunLen, c);return;
}

进入binarySort方法:

8、来到binarySort:

binary是二进制、二元的意思,这里应该是二分法插入排序。

可以看看注解:

/*** Sorts the specified portion of the specified array using a binary* insertion sort.  This is the best method for sorting small numbers* of elements.  It requires O(n log n) compares, but O(n^2) data* movement (worst case).** If the initial part of the specified range is already sorted,* this method can take advantage of it: the method assumes that the* elements from index {@code lo}, inclusive, to {@code start},* exclusive are already sorted.** @param a the array in which a range is to be sorted* @param lo the index of the first element in the range to be sorted* @param hi the index after the last element in the range to be sorted* @param start the index of the first element in the range that is*        not already known to be sorted ({@code lo <= start <= hi})* @param c comparator to used for the sort*/

翻译一下:

/*** 使用二进制对指定数组的指定部分进行排序* 插入排序。 这是对小数字进行排序的最佳方法* 的元素。 它需要 O(n log n) 比较,但 O(n^2) 数据*移动(最坏情况)。** 如果指定范围的初始部分已经排序,* 此方法可以利用它:该方法假定* 从索引 {@code lo}(包括)到 {@code 开始} 的元素,* 独家已排序。** @param要在其中对范围进行排序的数组* @param lo 要排序的范围内第一个元素的索引* @param 嗨,要排序的范围内最后一个元素之后的索引* @param开始范围内第一个元素的索引* 尚不知道已排序 ({@code lo <= 开始 <= hi})* @param c比较器用于排序*/

来看看参数:a时数组, lo是0, hi是len, lo + initRunLen = 0 + 2 = 2, c是接口实现类。

@SuppressWarnings("fallthrough")
private static  void binarySort(T[] a, int lo, int hi, int start,Comparator c) {assert lo <= start && start <= hi;if (start == lo)start++;for ( ; start < hi; start++) {T pivot = a[start];// Set left (and right) to the index where a[start] (pivot) belongsint left = lo;int right = start;assert left <= right;/** Invariants:*   pivot >= all in [lo, left).*   pivot <  all in [right, start).*/while (left < right) {int mid = (left + right) >>> 1;if (c.compare(pivot, a[mid]) < 0)right = mid;elseleft = mid + 1;}assert left == right;/** The invariants still hold: pivot >= all in [lo, left) and* pivot < all in [left, start), so pivot belongs at left.  Note* that if there are elements equal to pivot, left points to the* first slot after them -- that's why this sort is stable.* Slide elements over to make room for pivot.*/int n = start - left;  // The number of elements to move// Switch is just an optimization for arraycopy in default caseswitch (n) {case 2:  a[left + 2] = a[left + 1];case 1:  a[left + 1] = a[left];break;default: System.arraycopy(a, left, a, left + 1, n);}a[left] = pivot;}
}

太长了,慢慢分析吧:

①先断言:assert lo <= start && start <= hi,这里的start也就是runHi + lo = 2肯定是可以满足的

②判断start和lo是不通过的,跳;

发现里面又出现之前不认识的内容:

public static void main(String[] args) {System.out.println(4 >> 1);System.out.println(4 >>> 1);System.out.println(4 << 1);//  System.out.println(4 <<< 1);
}

就是>>>>>有什么区别?有没有<<<

发现是没有<<<的,写了的话会出现Expression expected警告,会报错。

<<肯定是左移了,>>是右移。没想到>>>也是右移。查了一下发现>>>>>不一样的,

对于正数右移,高位补0.

>>是有符号右移,对于负数,那么右移后,高位补1;>>>是无符号右移(又回到了学汇编的那个夏天),也就是无论正数还是负数,右移都是高位补0.

那么继续分析吧③

binarySort这个方法实际上就是对数据进行二分法排序,搬迁数据时会调用c++代码进行调用,下面详细解析一下:

for ( ; start < hi; start++) { //2 < 6? yesT pivot = a[start];//pivot = a[2] = 5 Ⅱ、 = -6// Set left (and right) to the index where a[start] (pivot) belongsint left = lo;//left = 0 Ⅱ、=0int right = start;// right = 2 Ⅱ、=3assert left <= right;//0 < 2? -> yes Ⅱ、=> 0 <= 3 ok/** Invariants:*   pivot >= all in [lo, left).*   pivot <  all in [right, start).*/while (left < right) {//0 < 2 ? yes Ⅱ、=>yesint mid = (left + right) >>> 1;// mid = (0 + 2) / 2 = 1,a = {-2,1,5,-6,3,9} Ⅱ、mid=1if (c.compare(pivot, a[mid]) < 0)//5 -( 1) = 4 < 0 ? ->false Ⅱ、-6<1?yesright = mid;//Ⅱ、right=1elseleft = mid + 1;//left = 1+1=2}assert left == right; // 2 == 2 ? => okⅡ、right=0/** The invariants still hold: pivot >= all in [lo, left) and* pivot < all in [left, start), so pivot belongs at left.  Note* that if there are elements equal to pivot, left points to the* first slot after them -- that's why this sort is stable.* Slide elements over to make room for pivot.*/int n = start - left;  // The number of elements to move n = 2 - 2 = 0Ⅱ、n=3-0=3// Switch is just an optimization for arraycopy in default caseswitch (n) {//n = 0。注意为什么有3个选项?因为native方法不能频繁调用,影响性能。case 2:  a[left + 2] = a[left + 1];//例如[1,2,3,5,6,4],lt=3,st=5,a[5]=6,再穿透到n=1的casecase 1:  a[left + 1] = a[left];//例如:[1,2,3,4,6,5],lt=4,st=5,5-4=1,此时就不需要调用耗性能的JNI了break;default: System.arraycopy(a, left, a, left + 1, n);//(a,2,a,3,0)//Ⅱ、(a,0,a,1,3),a = {-2,1,5,-6,3,9}//这里的arraycopy是什么意思?看到是native方法,看来是调用了c++应该是搬迁数据?应该是的。可以看看代码解析:arraycopy(src:a, srcPos:0, dest:a, destPos:1, length:3);之前也写过类似的,应该是将[0,2]的内容搬迁到[1,3]里,看后面的就知道了。}a[left] = pivot;//a[2] = 5 这里和二分法很像,看这里就知道了,将a[0] = pivot=-6

这里是升序,那么不断进行二分法,退出后就会得到升序好了的数组a了。

    };return;

然后就退出来了。此时数组已经排序完毕。

然后就回到Tim.sort-回到->Arrays.sort->end

相关内容

热门资讯

欢乐颂的经典台词有哪些 欢乐颂的经典台词有哪些  《欢乐颂》讲述了同住在欢乐颂小区22楼的五个来自不同家庭、性格迥异的女孩们...
女儿出嫁父母致辞 女儿出嫁父母致辞女儿出嫁父母致辞中国有句老话——“当众教子,背后教妻”。因此,在今天这个场合,请允许...
六一主持词 六一主持词15篇  主持词分为会议主持词、晚会主持词、活动主持词、婚庆主持词等。在当今社会生活中,主...
教育机构开业主持词 教育机构开业主持词  主持词分为会议主持词、晚会主持词、活动主持词、婚庆主持词等。在当今不断发展的世...
琅玡榜经典台词 琅玡榜经典台词  1、人只会被朋友背叛,敌人是永远都没有‘出卖’和‘背叛’的机会的。  2、只要你没...
王家卫经典台词 王家卫经典台词  王家卫相信大家都熟悉,他是香港电影导演、监制及编剧,擅长文艺电影。给我们带来了许多...
甄嬛传的经典台词合集 甄嬛传的经典台词合集  在社会发展不断提速的今天,需要使用台词的场合越来越多,台词是用以展示剧情,刻...
追悼会告别仪式主持词 追悼会告别仪式主持词  一位熟识的人突然离开,我们的心情总是沉重,怀着不舍之情为他办一场追悼会,对追...
mc天开场麦词 mc天开场麦词集锦  篇一:MC麦词开场  1.在这个穿越时尚魅力的快乐都市 让我的音乐点缀魅力的极...
《青瓷》经典台词 《青瓷》经典台词  1、什么是哥们儿?一起扛过枪,一起下过乡,一起同过窗,一起嫖过娼,一起分过脏。 ...
婚礼家长致辞 婚礼家长致辞六篇  篇一:婚礼家长致辞  我女儿与××先生结为百年夫妻,身为父母感到十分高兴。他们透...
会开场白主持词   主持人男:时光荏苒,青春在这里绽放,为了理想我们在这里摩拳擦掌  主持人女:岁月更迭,梦想依然灿...
中式浪漫婚礼主持词 中式浪漫婚礼主持词  主持人在台上表演的灵魂就表现在主持词中。在当今中国社会,各种场合中活跃现场气氛...
学术年会主持词 学术年会主持词范文(精选10篇)  根据活动对象的不同,需要设置不同的主持词。现今社会在不断向前发展...
年会董事长致辞 年会董事长致辞15篇  在我们平凡的日常里,大家或多或少都用到过致辞吧,致辞讲求条理性,有思路、层次...
六一文艺汇演主持稿 六一文艺汇演主持稿六一文艺汇演主持稿1  甲:尊敬的各位领导、各位老师  乙:亲爱的同学们  合:大...
公司元旦致辞 公司元旦致辞(精选10篇)  在平日的学习、工作和生活里,大家都对致辞很是熟悉吧,致辞要注意人物的身...
欢喜姻缘--较婚礼主持词 欢喜姻缘--较实用的婚礼主持词  尊敬的各位来宾、亲爱的朋友们、女士们、先生们大家上午好!  欢迎您...
企业年终颁奖晚会主持词   主持人:又是一年芳草绿,又是一年逢春意,又是一年春风起,又是一年听春雨。先生们、女士们 、各位领...
婚礼新郎致辞 婚礼新郎致辞列位宾客:各人午时好!本日是我儿子x与xx喜结良缘的大喜日子,承蒙列位宾客远道而来介入我...