jdk8 Arrays.sort()实现分析
Arrays.sort()根据所需要排序的数组的特点会选择不同的排序算法。
第一种情况,当被排序的数组长度小于47并且排序是从数组起始位置开始的时候,采用的排序方式是最简单的插入排序。
for (int i = left, j = i; i < right; j = ++i) {int ai = a[i + 1];while (ai < a[j]) {a[j + 1] = a[j];if (j-- == left) {break;}}a[j + 1] = ai;
}
第二种情况,当被排序的数组长度小于47但排序不是从数组起始位置开始的时候,那么就会选择成对插入排序的方式进行排序。
do {if (left >= right) {return;}} while (a[++left] >= a[left - 1]);for (int k = left; ++left <= right; k = ++left) {int a1 = a[k], a2 = a[left];if (a1 < a2) {a2 = a1; a1 = a[left];}while (a1 < a[--k]) {a[k + 2] = a[k];}a[++k + 1] = a1;while (a2 < a[--k]) {a[k + 1] = a[k];}a[k + 1] = a2;}int last = a[right];while (last < a[--right]) {a[right + 1] = a[right];}a[right + 1] = last;
}
成对插入排序的方式在插入排序的基础上同时对两个元素进行插入排序,在之前的基础上第二个也就是较小的数可以在较大数的基础上进行插入,减少了比较次数。
第三种情况和第四种排序算法的选择发生在当数组元素个数大于47小于286的时候。
首先,会对数组相应的进行切分。在这里的切分方式选择取数组中点,前后七分之一的方式相应得到五点,并对这五点进行排序,类似希尔排序。
int e3 = (left + right) >>> 1; // The midpoint
int e2 = e3 - seventh;
int e1 = e2 - seventh;
int e4 = e3 + seventh;
int e5 = e4 + seventh;
// Sort these elements using insertion sort
if (a[e2] < a[e1]) { int t = a[e2]; a[e2] = a[e1]; a[e1] = t; }if (a[e3] < a[e2]) { int t = a[e3]; a[e3] = a[e2]; a[e2] = t;if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
}
if (a[e4] < a[e3]) { int t = a[e4]; a[e4] = a[e3]; a[e3] = t;if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }}
}
if (a[e5] < a[e4]) { int t = a[e5]; a[e5] = a[e4]; a[e4] = t;if (t < a[e3]) { a[e4] = a[e3]; a[e3] = t;if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }}}
}
算法的选择发生在此处,如果选择到的五个切分点不存在相同的,那么将会采用双轴快速排序的方式进行排序。
outer:
for (int k = less - 1; ++k <= great; ) {int ak = a[k];if (ak < pivot1) { // Move a[k] to left parta[k] = a[less];/** Here and below we use "a[i] = b; i++;" instead* of "a[i++] = b;" due to performance issue.*/a[less] = ak;++less;} else if (ak > pivot2) { // Move a[k] to right partwhile (a[great] > pivot2) {if (great-- == k) {break outer;}}if (a[great] < pivot1) { // a[great] <= pivot2a[k] = a[less];a[less] = a[great];++less;} else { // pivot1 <= a[great] <= pivot2a[k] = a[great];}/** Here and below we use "a[i] = b; i--;" instead* of "a[i--] = b;" due to performance issue.*/a[great] = ak;--great;}
}// Swap pivots into their final positions
a[left] = a[less - 1]; a[less - 1] = pivot1;
a[right] = a[great + 1]; a[great + 1] = pivot2;// Sort left and right parts recursively, excluding known pivots
sort(a, left, less - 2, leftmost);
sort(a, great + 2, right, false);
双轴快排相较于普通快排,多了一个比对对象,将数组划分为了三个区间,具体如下。
/** Partitioning:** left part center part right part* +--------------------------------------------------------------+* | < pivot1 | pivot1 <= && <= pivot2 | ? | > pivot2 |* +--------------------------------------------------------------+* ^ ^ ^* | | |* less k great** Invariants:** all in (left, less) < pivot1* pivot1 <= all in [less, k) <= pivot2* all in (great, right) > pivot2** Pointer k is the first index of ?-part.*/
一轮双轴快排结束将会被分为小于基准1,大于基准1小于基准2,大于基准2三个分区,之后继续对这三个分区继续分别排序。
其中,如果中间的部分过于庞大,还存在着优化方案,当选择的两个基准中的插值过大的时候,中间部分也许会过于庞大(超过整个排序部分的七分之四),将会针对这一区间内的重复键进行优化。
outer:
for (int k = less - 1; ++k <= great; ) {int ak = a[k];if (ak == pivot1) { // Move a[k] to left parta[k] = a[less];a[less] = ak;++less;} else if (ak == pivot2) { // Move a[k] to right partwhile (a[great] == pivot2) {if (great-- == k) {break outer;}}if (a[great] == pivot1) { // a[great] < pivot2a[k] = a[less];/** Even though a[great] equals to pivot1, the* assignment a[less] = pivot1 may be incorrect,* if a[great] and pivot1 are floating-point zeros* of different signs. Therefore in float and* double sorting methods we have to use more* accurate assignment a[less] = a[great].*/a[less] = pivot1;++less;} else { // pivot1 < a[great] < pivot2a[k] = a[great];}a[great] = ak;--great;}
}
在这里中间部分会继续被分为三个部分,如下图。
/** Partitioning:** left part center part right part* +----------------------------------------------------------+* | == pivot1 | pivot1 < && < pivot2 | ? | == pivot2 |* +----------------------------------------------------------+* ^ ^ ^* | | |* less k great** Invariants:** all in (*, less) == pivot1* pivot1 < all in [less, k) < pivot2* all in (great, *) == pivot2** Pointer k is the first index of ?-part.*/
其中与基准1和基准2 相同的键将会被分到前后两个分区,并会被从接下来要继续递归排序的中间分区中剔除,尽可能减少了中间分区的大小。
相较于第三种情况,第四种情况排序算法的选择在之前选择完毕五个基准点之后,如果任意两个点存在相同的情况,选择更合适存在大量相同键的三项切分快速排序。
for (int k = less; k <= great; ++k) {if (a[k] == pivot) {continue;}int ak = a[k];if (ak < pivot) { // Move a[k] to left parta[k] = a[less];a[less] = ak;++less;} else { // a[k] > pivot - Move a[k] to right partwhile (a[great] > pivot) {--great;}if (a[great] < pivot) { // a[great] <= pivota[k] = a[less];a[less] = a[great];++less;} else { // a[great] == pivot/** Even though a[great] equals to pivot, the* assignment a[k] = pivot may be incorrect,* if a[great] and pivot are floating-point* zeros of different signs. Therefore in float* and double sorting methods we have to use* more accurate assignment a[k] = a[great].*/a[k] = pivot;}a[great] = ak;--great;}
}
相比双轴快排,三向切分快排只选取了一个基准点来对数组进行切分,而相比普通快排,在大于基准点和小于基准点两个分区的基础上,增加了与基准点相同的分区这一中间分区。
/** Partitioning degenerates to the traditional 3-way* (or "Dutch National Flag") schema:** left part center part right part* +-------------------------------------------------+* | < pivot | == pivot | ? | > pivot |* +-------------------------------------------------+* ^ ^ ^* | | |* less k great** Invariants:** all in (left, less) < pivot* all in [less, k) == pivot* all in (great, right) > pivot** Pointer k is the first index of ?-part.*/
上述情况都是在被排序的数组长度小于286的情况下的选择,当长度大于286之后,将会根据数组按照顺序大小升降的改变次数选择具体的算法。
int[] run = new int[MAX_RUN_COUNT + 1];
int count = 0; run[0] = left;// Check if the array is nearly sorted
for (int k = left; k < right; run[count] = k) {if (a[k] < a[k + 1]) { // ascendingwhile (++k <= right && a[k - 1] <= a[k]);} else if (a[k] > a[k + 1]) { // descendingwhile (++k <= right && a[k - 1] >= a[k]);for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {int t = a[lo]; a[lo] = a[hi]; a[hi] = t;}} else { // equalfor (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) {if (--m == 0) {sort(a, left, right, true);return;}}}/** The array is not highly structured,* use Quicksort instead of merge sort.*/if (++count == MAX_RUN_COUNT) {sort(a, left, right, true);return;}
}
在大于286的数组中,如果升降情况改变次数大于67时,那么可以推定该数组整体是无序的,那么此时更适合之前两种快速排序算法,反之,由于快速排序在有序的数组中表现不好,对于大于286总体有序的数组更合适归并排序,也是这里出现的第五种算法。
byte odd = 0;
for (int n = 1; (n <<= 1) < count; odd ^= 1);// Use or create temporary array b for merging
int[] b; // temp array; alternates with a
int ao, bo; // array offsets from 'left'
int blen = right - left; // space needed for b
if (work == null || workLen < blen || workBase + blen > work.length) {work = new int[blen];workBase = 0;
}
if (odd == 0) {System.arraycopy(a, left, work, workBase, blen);b = a;bo = 0;a = work;ao = workBase - left;
} else {b = work;ao = 0;bo = workBase - left;
}// Merging
for (int last; count > 1; count = last) {for (int k = (last = 0) + 2; k <= count; k += 2) {int hi = run[k], mi = run[k - 1];for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {b[i + bo] = a[p++ + ao];} else {b[i + bo] = a[q++ + ao];}}run[++last] = hi;}if ((count & 1) != 0) {for (int i = right, lo = run[count - 1]; --i >= lo;b[i + bo] = a[i + ao]);run[++last] = right;}int[] t = a; a = b; b = t;int o = ao; ao = bo; bo = o;
}
精妙之处在于,相比普通归并排序2分分组不同,由于先前已经记录了各个小型有序分区的情况,只需要在这基础上将各个小的有序分区作为归并排序的起点即可。
jdk8 Arrays.sort()实现分析相关推荐
- java arrays.sort() c_正面刚算法-Java中Arrays.sort()(一)
最近一直在看关于排序相关的算法,从O(n²)的冒泡.插入.选择到O(nlog(n))的归并.快排.再到桶排序.计数排序.基数排序.各个算法都有自己的优点和缺点,那么jdk中关于这种底层的算法是怎么实现 ...
- java中Arrays.sort()实现原理
2019独角兽企业重金招聘Python工程师标准>>> 先在网上找到一些说法: java中Arrays.sort使用了两种排序方法,快速排序和优化的合并排序. 快速排序主要是对哪些基 ...
- Java的Arrays.sort()良心总结
C语言的stilib.h头文件中有qsort(),C++的STL库中有sort(),这些封装好的排序函数让我们避免了比赛手写排序,给我们解题带来了的便利.在Java的Arrays类中也封装好了类似的方 ...
- java arrays.sort() c_5.4 (Java学习笔记)集合的排序(Collections.sort(),及Arrays.sort())...
1.Comparable接口 这个接口顾名思义就是用于排序的,如果要对某些对象进行排序,那么该对象所在的类必须实现 Comparabld接口.Comparable接口只有一个方法CompareTo() ...
- java list逆序_Java的数组和list升序,降序,逆序函数Collections.sort和Arrays.sort的使用...
list升序,降序,逆序 Listlist =new ArrayList(); //如果list是 5 7 2 6 8 1 4 1.升序: Collections.sort(list) //list: ...
- Java中的Arrays.sort(int[])
Arrays.sort(int[]) Arrays.sort(int[])使用的是quicksort+merge sort. 使用quicksort:当数组长度比较小(right-left<28 ...
- JDK8 Stream 数据流效率分析
JDK8 Stream 数据流效率分析 Stream 是Java SE 8类库中新增的关键抽象,它被定义于 java.util.stream (这个包里有若干流类型: Stream<T> ...
- Java数组排序: Array-ArrayList-List-Collections.sort()/List.sort()/Arrays.sort()
文章目录 ArrayList/List 的排序:Collections.sort()/List.sort() Array 的排序:Arrays.sort() 此文首发于我的Jekyll博客:zhang ...
- Java Arrays.Sort方法重写
当原始的java sort方法无法满足我们的需求时候,我们需要自定义一些排序方法,此时需要重写Array.sort方法重写. 模板代码如下,默认是从小到大排序的,如果想从大到小,把a-b换为b-a即可 ...
最新文章
- Windows如何 cmd 查找文件路径 开机启动 CMD语音播放 CMD切换到管理员!
- python画海绵宝宝_一步一步教你画章鱼哥怎么画好看?教你学画海绵宝宝的章鱼哥简笔画!...
- OpenFOAM计算时,同时将结果输出到:计算窗口+文件
- 什么原因成就了一位优秀的程序员?
- c++ post请求_Golang GinWeb框架5绑定请求字符串/URI/请求头/复选框/表单类型
- conda h5py_修改conda安装路径
- Spring的@Transactional事务注意事项
- 关于微信支付接口,curl错误代码58
- 升级到Firefox 3.0后解决扩展版本不兼容的方法
- Tomcat 设置系统默认文件编码
- mysql sqlyog讲解_MySQL与sqlyog安装教程图文详解
- 怎么用odbc连接mysql数据库连接_怎么用odbc连接mysql数据库
- Fatal error: Out of memory (allocated 2252140544) (tried to allocate 67108864 bytes)
- springMVC 用超链接做国际化
- 代码分析UEFI的执行流程
- RAKsmart:查询在线服务器的方法
- CTF Crypto中涉及的AES题目
- InnoDB 离线转储工具
- sata学习5:常用的概率函数
- Feedback Control of Dynamic Systems 7th
热门文章
- 诗与远方:无题(二十七)- 写给我妹妹的一首诗
- Spark GraphX算法 - Pregel算法
- getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getSchemeSpecificPart()返回内容解析
- tomcat和java安装,JavaWeb-Tomcat下载和安装
- 网页版本的飞行日志分析平台是_一个轻便的实时日志收集平台wslog
- raid5需要几块硬盘_Raid5盘阵2块硬盘损坏【热备盘未激活】数据恢复概述
- python安装到桌面的路径是什么_Python 获取windows桌面路径的5种方法小结
- win7分区c盘调整容量_C盘空间不足变红咋办?清理垃圾瘦身不如扩容,硬盘容量调整教程...
- 用Java描述数据结构之线性表的链式存储(链表),模拟LinkedList实现
- 知方可补不足~用xsl来修饰xml