Arrays.sort(Object[] arr)排序源码分析

本文详细解析了Java 1.8中Arrays.sort()方法的内部实现,介绍了TimSort算法,一种结合了归并排序和插入排序的高效算法。文章分析了TimSort在不同情况下的性能优势,包括在部分有序数组上的表现,以及如何在1.8及以后版本中启用此算法。

简介:
    在1.8之前的版本中对此方法进行了重写,如果还需要使用之前的排序算法,需要加上系统属性java.util.Arrays.useLegacyMergeSort=true,之前的排序使用优化后的归并排序,在1.8中使用TimSort算法来进行排序,TimSort算法是由Tim Peters在2002提出并首先实现在了phtyon中,是结合了合并排序(merge sort)和插入排序(insertion sort)的一种高效稳定的算法。算法原理看这里 https://blog.youkuaiyun.com/yangzhongblog/article/details/8184707

Arrays.sort(Object[] a)分析
1、 方法声明

/**
 * 根据Comparable的自然排序将给定数组升序排序。所有元素必须实现Comparable 接口
 * 而且,所有元素能相互比较,不然可能会抛出ClassCastException异常
 *
 * 此排序确保是稳定的,如果两个元素e1.equals(e2),那么他们不会被再次排序
 *
 * 该实现是一个稳定、自适应、重复的归并排序,当给定数组是局部排序的数组时它的时间复杂度比 nlogn更小,
 * 然而当给定数组是随机排序,它与传统的归并排序性能相同。如果给定数组大体有序,该算法只需要大概n次比较即可完成排序。
 *
 * 该算法在输入数组为升序或降序时都能发挥优势,并且在该数组部分有序的情况下也能发挥其优势,
 * 它能通过简单连接即可很好的完成将多个数组进行合并
 */
public static void sort(Object[] a) {
//如果要兼容1.8之前的排序程序,在系统属性中需要添加属性,如:java -jar -Djava.Arrays.uselegacyMergeSort=true xx.jar
    if (LegacyMergeSort.userRequested)
        legacyMergeSort(a);
    else
		//真正的TimSort算法实现在ComparableTimSort中
        ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
	}

2、 ComparableTimSort.sort(Object[] a, int lo, int hi, Object[] work, int workBase, int workLen)分析

/**
 * 将数组指定范围内的元素排序
 *
 * [@param](https://my.oschina.net/u/2303379) a 被排序数组
 * [@param](https://my.oschina.net/u/2303379) lo 排序开始元素index
 * [@param](https://my.oschina.net/u/2303379) hi 排序结束元素index,不包含此index
 * [@param](https://my.oschina.net/u/2303379) work a workspace array (slice)
 * [@param](https://my.oschina.net/u/2303379) workBase origin of usable space in work array
 * @param workLen usable size of work array
 * @since 1.8
 */
static void sort(Object[] a, int lo, int hi, Object[] work, int workBase, int workLen) {
	
	//保证数组的合法性
    assert a != null && lo >= 0 && lo <= hi && hi <= a.length;
	
    int nRemaining  = hi - lo;
	//对于只有0|1个元素的数组,不需要进行排序
    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);
		//使用二叉插入排序对在initRunLen后的元素进行排序
        binarySort(a, lo, hi, lo + initRunLen);
        return;
    }

    /**
     * March over the array once, left to right, finding natural runs,
     * extending short natural runs to minRun elements, and merging runs
     * to maintain stack invariant.
     */
	 //构造ComparableTimSort对象
    ComparableTimSort ts = new ComparableTimSort(a, work, workBase, workLen);
	//计算最小run的长度
    int minRun = minRunLength(nRemaining);
    do {
        // Identify next run
		//计算当前排序的run的长度,如果为递减数组则翻转
        int runLen = countRunAndMakeAscending(a, lo, hi);

        // If run is short, extend to min(minRun, nRemaining)
		//如果当前run的长度小于minRun,则进行扩展,在扩展过程中使用二叉排序来排序扩展的的元素
        if (runLen < minRun) {
            int force = nRemaining <= minRun ? nRemaining : minRun;
            binarySort(a, lo, lo + force, lo + runLen);
            runLen = force;
        }

        // Push run onto pending-run stack, and maybe merge
		//将此run放入栈中
        ts.pushRun(lo, runLen);
		//执行合并逻辑,合并的时候也做了一些优化
        ts.mergeCollapse();

        // Advance to find next run
        lo += runLen;
        nRemaining -= runLen;
    } while (nRemaining != 0);

    // Merge all remaining runs to complete sort
    assert lo == hi;
	//保证最后的run都被合并
    ts.mergeForceCollapse();
    assert ts.stackSize == 1;
}

转载于:https://my.oschina.net/u/3286119/blog/2055991

<think>好的,我现在需要回答用户的问题:“二维数组直接使用Arrays.sort(arr);会是什么结果”。用户之前已经询问过如何根据二维数组的第二个值进行升序排序,现在他们想知道如果直接使用Arrays.sort()而不自定义比较器会发生什么。 首先,我得回忆一下Java中Arrays.sort()方法对二维数组的处理方式。用户可能对默认排序的逻辑不太清楚,所以需要详细解释。Java中的二维数组实际上是一个数组的数组,每个元素是一个一维数组。当直接调用Arrays.sort(arr)时,这里的arr是一个二维数组,比如int[][]或者Integer[][]。 接下来,我需要确定默认的比较器是如何工作的。对于对象数组,Arrays.sort()使用自然排序或者提供的比较器。对于基本类型的数组(如int[][]),Arrays.sort()使用双轴快速排序,而对于对象数组(如Integer[][]),则使用归并排序或TimSort,并根据元素的自然顺序进行排序。然而,二维数组的元素本身是数组,而数组在Java中是对象,但并没有实现Comparable接口,所以这里可能存在一些陷阱。 需要验证一下,当对二维数组(比如int[][])使用Arrays.sort()时,是否会抛出异常,或者如何进行比较。例如,如果arr是int[][],那么每个元素是一个int[],而int[]是对象,但int[]类并没有实现Comparable接口,所以调用Arrays.sort(arr)时,会抛出ClassCastException,因为无法将int[]转换为Comparable。不过,可能用户实际使用的是Integer[][],这时候每个元素是Integer[],同样的问题,因为Integer[]也没有实现Comparable。因此,无论是int[][]还是Integer[][],直接使用Arrays.sort()都会导致错误,因为数组元素无法被比较。 不过,我需要实际测试一下,或者回忆Java文档中的说明。根据Java文档,Arrays.sort(Object[] a)要求数组元素实现Comparable接口,否则会抛出ClassCastException。因此,当对二维数组(如Integer[][]或int[][])直接调用Arrays.sort()时,由于内部的数组元素(如Integer[]或int[])没有实现Comparable,会导致运行时异常。 但用户可能在测试时使用了不同的情况,比如在某些情况下可能不会报错?比如,如果二维数组中的子数组都是同一个对象引用,但这种情况比较特殊。需要明确告诉用户直接调用Arrays.sort()会导致错误,并解释原因。 另外,用户可能在之前的例子中使用了自定义比较器,所以现在他们想知道如果不使用比较器会发生什么。需要明确指出,直接调用会出错,因为没有定义如何比较两个一维数组。 总结回答的步骤应该是:首先说明直接调用Arrays.sort()的结果是抛出ClassCastException,因为数组元素无法比较;然后解释原因,即数组元素没有实现Comparable接口;接着可能给出示例代码和错误信息;最后建议正确的做法是使用自定义比较器,如之前的问题所示。 需要确保语言清晰,避免技术术语过多,但又要准确。可能需要给出一个简单的代码示例,展示直接调用时的错误,并解释错误信息。同时,提醒用户之前的方法是正确的,必须提供比较器来定义排序规则。 最后,检查是否有遗漏点,比如对于不同数据类型的二维数组(如String[][])是否会有不同表现,但用户问题中的上下文可能集中在Integer或int类型的二维数组,所以可能不需要扩展讨论其他类型,除非有必要。</think>--- ### 直接使用 `Arrays.sort(arr)` 对二维数组排序的结果 --- #### **1. 结果分析** 若直接对二维数组调用 `Arrays.sort(arr)`(例如 `int[][]` 或 `Integer[][]`),会抛出 **`ClassCastException`** 异常。 原因:二维数组的每个元素是另一个数组(例如 `int[]` 或 `Integer[]`),而数组类型 **没有实现 `Comparable` 接口**,无法直接比较大小。 --- #### **2. 代码验证** ```java import java.util.Arrays; public class Main { public static void main(String[] args) { int[][] arr = { {5, 9}, {2, 3}, {7, 1}, {4, 6} }; // 尝试直接排序(错误用法) Arrays.sort(arr); // 抛出 ClassCastException } } ``` --- #### **3. 错误信息** 运行时报错: ```java Exception in thread "main" java.lang.ClassCastException: [I cannot be cast to java.lang.Comparable ``` - **`[I`** 是 JVM 中 `int[]` 的类型描述符。 - 错误原因:`int[]` 未实现 `Comparable`,无法被强制转换为可比较类型。 --- #### **4. 根本原因** - **数组是对象**:`int[]`、`Integer[]` 等数组类型本身是对象,但未实现 `Comparable` 接口。 - **默认比较规则缺失**:`Arrays.sort()` 需要元素可比较(自然顺序),但数组之间没有默认的大小定义。 --- #### **5. 正确方法** 必须通过 **自定义 `Comparator`** 明确指定二维数组的排序规则(例如按子数组的第二个元素排序): ```java // 对 Integer[][] 排序 Arrays.sort(arr, (a, b) -> Integer.compare(a[1], b[1])); // 对 int[][] 排序(需转换为 Integer[][] 或手动实现比较逻辑) ``` --- #### **6. 总结** - **直接调用 `Arrays.sort(arr)` 会失败**:二维数组元素(子数组)不可直接比较。 - **必须自定义比较器**:明确指定按子数组的某个元素或规则排序。 - **扩展思考**:若子数组是 `String[]` 或其他对象数组,同样需自定义比较逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值