在学习过程中观察到Arrays.sort(arr)算法可以直接进行排序,但不清楚底层的代码逻辑是什么样子,记得自己之前在面试题里面也有面试官问这个问题,只能说研究之后发现还是比较复杂的,并不是网上说的快排或者二分插入之类的。

首先看源码:

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

它调用了DualPivotQuicksort的sort方法,乍一看以为是快排,这是很多网上的博主的说法,继续点开向下看(代码太长,没耐心看可以直接跳过该段代码QWQ):

    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;
} /*
* Index run[i] is the start of i-th run
* (ascending or descending sequence).
*/
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]) { // ascending
while (++k <= right && a[k - 1] <= a[k]);
} else if (a[k] > a[k + 1]) { // descending
while (++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 { // equal
for (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;
}
} // Check special cases
// Implementation note: variable "right" is increased by 1.
if (run[count] == right++) { // The last run contains one element
run[++count] = right;
} else if (count == 1) { // The array is already sorted
return;
} // Determine alternation base for merge
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;
}
}

代码很长,简要翻译过来,这里分了好几种情况:


  1. 数组长度小于286

    这里又会调用一个sort方法,点开该sort(),又会划分情况:

    • 数组长度小于47,

      当leftmost(导入的一个布尔参数)为true,则使用直接插入排序;

      否则会调用另一种插入办法,这里可以观察到一个注释:

       /*
      * Every element from adjoining part plays the role
      * of sentinel, therefore this allows us to avoid the
      * left range check on each iteration. Moreover, we use
      * the more optimized algorithm, so called pair insertion
      * sort, which is faster (in the context of Quicksort)
      * than traditional implementation of insertion sort.
      */

      大致意思是:相邻部分的每个元素都扮演着哨兵的角色,因此这允许我们避免在每次迭代中进行左范围检查。此外,我们使用了更优化的算法,即所谓的成对插入排序它比插入排序的传统实现更快(在快速排序的上下文中)。

      不过注意到,原函数参数传参在这里leftmost为true,所以一定是直接插入排序,以上作为了解。

    • 数组长度大于47,采用一种快速排序的办法,这里因为代码太长,学艺不精,参考了一下白春雨大佬的文章分析:

      至于大过INSERTION_SORT_THRESHOLD(47)的,用一种快速排序的方法:

      1.从数列中挑出五个元素,称为 “基准”(pivot);

      2.重新排序数列,所有元素比基准值的摆放在基准前面,所有元素比基准值的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;

      3.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。


  2. 当数组长度大于286时

    此时回到那段很长很长的代码段,在判断小于286的长度数组之后,从注解中:

    // Check if the array is nearly sorted

    这里是指检查数组元素是不是快要排列好了,或者书面一点说,是不是有一定结构了,然后看后面的for循环,注意到一段代码:

                if (++count == MAX_RUN_COUNT) {
    sort(a, left, right, true);
    return;
    }

    这里的sort和我们上面在数组长度小于286时的那个sort方法是同一个方法,而针对这个count,是用来记录逆序组的,打个比方:

    此时有一个数组为1 5 6 9 8 7 2 3

    当数组认定我们的顺序应该为升序时,从第一个数开始数,此时9 8 7 2为降序,这就是逆序,将这四个数组合成一个组称为逆序组,然后再从3开始往后看。

    当统计到一个逆序组时,count++,所以可以看出,count是用来记逆序组的,那么逆序组越多,这个结构就越混乱

    MAX_RUN_COUNT == 67 ,因此当count一直加到67时,就说明已经在一个混乱的临界值了,此时执行sort()方法

    通过这一段分析,我们理一下思路:

    ​ 如果数组能运行到这里,说明数组的长度大于等于286。符合该条件时,我们要判断这个数组是否有一定的结构:

    (1)count<67,说明不是那么混乱,有一定结构,跳过;

    (2)count>=67,说明已经混乱了,没有结构,执行sort方法,而已知数组长度大于等于286,那么必然大于47,一定执行快速排序

    跳过之后,经过代码的一大堆前置操作,最后看见下面的代码里面一行注释:

    //Merging

    显然,这里后面用到归并排序了,不详细赘述。


    好了,最后总结:

    1. 数组长度小于286时,根据数组长度,分两种情况:

      • 数组长度小于47,使用直接插入排序
      • 数组长度大于47,使用快速排序
    2. 数组长度大于286时,根据数组排序情况,分两种情况:
      • 数组内顺序较为混乱,即count逆序组数大于等于67,使用快速排序
      • 数组内有一定顺序,即count逆序组数小于67,使用归并排序

    欢迎各位大佬讨论

    参考资料:

    《Java的Arrays.sort()方法到底用的什么排序算法》 https://www.cnblogs.com/baichunyu/p/11935995.html 作者:白春雨

Arrays.sort(arr)是什么排序的更多相关文章

  1. Arrays.Sort()中的那些排序算法

    本文基于JDK 1.8.0_211撰写,基于java.util.Arrays.sort()方法浅谈目前Java所用到的排序算法,仅个人见解和笔记,若有问题欢迎指证,着重介绍其中的TimSort排序,其 ...

  2. Arrays.sort(new String[]{"aaa"}); 排序方法

    private static void mergeSort(Object[] src, Object[] dest, int low, int high, int off) { int length ...

  3. Java找N个数中最小的K个数,PriorityQueue和Arrays.sort()两种实现方法

    最近看到了 java.util.PriorityQueue.刚看到还没什么感觉,今天突然发现他可以用来找N个数中最小的K个数. 假设有如下 10 个整数. 5 2 0 1 4 8 6 9 7 3 怎么 ...

  4. 微信公众号接入之排序问题小记 Arrays.sort()

    微信公众号作为强大的自媒体工具,对接一下是很正常的了.不过这不是本文的方向,本文的方向公众号接入的排序问题. 最近接了一个重构的小项目,需要将原有的php的公众号后台系统,转换为java系统.当然,也 ...

  5. Java中sort实现降序排序

    利用Collections的reverseOrder方法: import java.util.Arrays; import java.util.Collections; public class Ma ...

  6. Java Arrays.sort()重写comparator方法

    先看一下接口 Arrays.sort(T[],Comparator<? super T> c); comparator要重写compare方法 compare方法大概长这样,返回值> ...

  7. 集合(一)-Java中Arrays.sort()自定义数组的升序和降序排序

    默认升序 package peng; import java.util.Arrays;  public class Testexample { public static void main(Stri ...

  8. java.util.Arrays.sort两种方式的排序(及文件读写练习)

    import java.io.*; import java.util.*; public class SortTest{ public static void main(String args[]) ...

  9. Java 使用Arrays.sort排序 从大到小排列

    前言 一般情况,我们在Java中给数组排序,比起自己写个冒泡排序,更加喜欢使用Java中自带的sort方法,也就是Arrays.sort方法 但是,这个方法只会将数组从小到大排列,如果我们需要从大到小 ...

随机推荐

  1. 【LeetCode】914. X of a Kind in a Deck of Cards 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 遍历 最大公约数 日期 题目地址: https:// ...

  2. 如何把 MySQL 备份验证性能提升 10 倍

    JuiceFS 非常适合用来做 MySQL 物理备份,具体使用参考我们的官方文档.最近有个客户在测试时反馈,备份验证的数据准备(xtrabackup --prepare)过程非常慢.我们借助 Juic ...

  3. Codeforces 919D:Substring(拓扑排序+DP)

    D. Substring time limit: per test3 seconds memory limit: per test256 megabytes inputstandard: input ...

  4. [开发配置]Linux系统配置开发环境

    deeplin系统配置开发环境 开发系统:deeplin 15.11 开发工具:Clion 2019.2 ; PyCharm 2019 ; Idea 2019;  Android Studio 开源库 ...

  5. Spring企业级程序设计 • 【第3章 面向切面编程】

    全部章节   >>>> 本章目录 3.1 AOP基本概念和术语 3.1.1 AOP概念 3.1.2 AOP的术语解释 3.1.3 通知类型介绍 3.1.4 通过AOP模拟事务操 ...

  6. CSS基础6之盒子模型1

    盒子概述 以下是盒子模型的一个图形解释 一.内边距(填充) 属性有: (1) padding  设置所有内边距 (2) padding-top  设置上边距 (3) padding-left 设置左边 ...

  7. docker安装minio

    目录 一.简介 二.docker安装 三.java中使用minio上传与下载 一.简介 MinIO 是在 GNU Affero 通用公共许可证 v3.0 下发布的高性能对象存储. 它是与 Amazon ...

  8. Visual Studio Code常用插件(持续更新)

    1.open in browser:使用快捷键在浏览器中打开html页面 在默认浏览器中打开html页面是Alt+B:在其他浏览器中显示是Shift+Alt+B:也可以右键单击html文件,在弹出的窗 ...

  9. springboot 配置 swagger2

    1.pom.xml 添加依赖 <!--swagger2 依赖--> <dependency> <groupId>io.springfox</groupId&g ...

  10. Python爬虫脚本 ,Uni-APP复选框做出双向绑定 ,Net5工作流建模 。的一点经验

    从业C#开发多年,现在也经常用到Python 做网络爬虫 ,用Uni-app做手机前端.攒了一点经验.供其他多语言开发程序员借鉴吧. Python做爬虫和其他的方式做爬虫最大的区别应该在于. Pyth ...