杰瑞科技汇

Java的Arrays.sort底层实现是怎样的?

Arrays.sort() 是 Java 中一个非常核心且常用的方法,它位于 java.util.Arrays 类中,用于对各种类型的数组进行排序,它的强大之处在于提供了多种重载形式,能够处理基本类型数组和对象数组,并且对于对象数组,它支持自定义排序规则。


基本概述

Arrays.sort() 方法的主要功能是对数组进行原地排序,这意味着它会直接修改传入的数组,而不是返回一个新的已排序的数组。

它的核心功能依赖于两种不同的排序算法:

  1. 基本类型数组:使用双轴快速排序,这是一种优化的快速排序算法,平均时间复杂度为 O(n log n),并且在大多数情况下性能非常出色。
  2. 对象数组:使用归并排序 的优化版本,称为 TimSort,TimSort 是一种混合排序算法,结合了归并排序的稳定性和插入排序对小数据集的高效性,它的时间复杂度也是 O(n log n),并且是稳定排序

稳定排序:如果两个元素相等,它们在排序后的相对顺序与排序前保持一致。 不稳定排序:不保证相等元素的相对顺序。


方法签名与重载

Arrays.sort() 提供了非常丰富的重载形式,主要可以分为以下几类:

a. 对基本类型数组进行排序

这是最简单的用法,直接对数组元素进行升序排序。

// 语法
Arrays.sort(primitiveArray);
// 示例
import java.util.Arrays;
public class PrimitiveSortExample {
    public static void main(String[] args) {
        int[] numbers = {5, 1, 9, 3, 7, 2, 8, 4, 6};
        System.out.println("排序前: " + Arrays.toString(numbers));
        Arrays.sort(numbers); // 对 int 数组进行升序排序
        System.out.println("排序后: " + Arrays.toString(numbers));
    }
}
// 输出:
// 排序前: [5, 1, 9, 3, 7, 2, 8, 4, 6]
// 排序后: [1, 2, 3, 4, 5, 6, 7, 8, 9]

支持的基本类型包括:int, long, short, byte, char, double, float, boolean (false 排在 true 前面)。

b. 对对象数组进行排序

当数组元素是对象时,Arrays.sort() 会要求这些对象必须实现 Comparable 接口。Comparable 接口定义了对象之间“自然排序”的规则。

// 语法
Arrays.sort(objectArray);
// 示例: 对 String 数组排序
// String 类已经实现了 Comparable<String> 接口
import java.util.Arrays;
public class ObjectSortExample {
    public static void main(String[] args) {
        String[] names = {"Charlie", "Alice", "David", "Bob"};
        System.out.println("排序前: " + Arrays.toString(names));
        Arrays.sort(names); // 按照字典序(自然顺序)排序
        System.out.println("排序后: " + Arrays.toString(names));
    }
}
// 输出:
// 排序前: [Charlie, Alice, David, Bob]
// 排序后: [Alice, Bob, Charlie, David]

如果尝试对一个没有实现 Comparable 接口的自定义对象数组调用 Arrays.sort(),程序会抛出 ClassCastException

class Person {
    String name;
    int age;
    // ... constructor, getters, setters
}
// Person 类没有实现 Comparable
Person[] people = {new Person("Alice", 30), new Person("Bob", 25)};
Arrays.sort(people); // 运行时抛出 ClassCastException

c. 对数组的指定范围进行排序

你可以只对数组的一部分进行排序,而保持其他部分不变。

// 语法
Arrays.sort(array, fromIndex, toIndex);
// 示例
import java.util.Arrays;
public class RangeSortExample {
    public static void main(String[] args) {
        int[] numbers = {5, 1, 9, 3, 7, 2, 8, 4, 6};
        System.out.println("排序前: " + Arrays.toString(numbers));
        // 对索引 2 到 6 (包含2, 不包含6) 的元素进行排序
        // 即对子数组 {9, 3, 7, 2} 进行排序
        Arrays.sort(numbers, 2, 6);
        System.out.println("排序后: " + Arrays.toString(numbers));
    }
}
// 输出:
// 排序前: [5, 1, 9, 3, 7, 2, 8, 4, 6]
// 排序后: [5, 1, 2, 3, 7, 9, 8, 4, 6]

注意fromIndex 是包含的,toIndex 是不包含的。

d. 使用自定义比较器进行排序

这是 Arrays.sort() 最强大的功能之一,当你需要自定义排序规则时,可以使用 Comparator 接口。

  1. 用于对象数组
// 语法
Arrays.sort(objectArray, comparator);
// 示例: 对 Person 对象数组按年龄排序
import java.util.Arrays;
import java.util.Comparator;
class Person {
    String name;
    int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return name + "(" + age + ")";
    }
}
public class ComparatorSortExample {
    public static void main(String[] args) {
        Person[] people = {
            new Person("Alice", 30),
            new Person("Bob", 25),
            new Person("Charlie", 35)
        };
        System.out.println("按年龄升序排序前: " + Arrays.toString(people));
        // 使用 lambda 表达式创建 Comparator
        Arrays.sort(people, Comparator.comparingInt(p -> p.age));
        System.out.println("按年龄升序排序后: " + Arrays.toString(people));
        // 按姓名降序排序
        Arrays.sort(people, Comparator.comparing(Person::getName).reversed());
        System.out.println("按姓名降序排序后: " + Arrays.toString(people));
    }
}
// 输出:
// 按年龄升序排序前: [Alice(30), Bob(25), Charlie(35)]
// 按年龄升序排序后: [Bob(25), Alice(30), Charlie(35)]
// 按姓名降序排序后: [Charlie(35), Bob(25), Alice(30)]
  1. 用于基本类型数组 (Java 8+): 虽然不能直接为基本类型数组传递 Comparator,但可以使用 IntStream, LongStream 等进行转换和排序。

    import java.util.Arrays;
    import java.util.Comparator;
    public class PrimitiveComparatorExample {
        public static void main(String[] args) {
            int[] numbers = {5, 1, 9, 3, 7, 2, 8, 4, 6};
            System.out.println("排序前: " + Arrays.toString(numbers));
            // 使用 IntStream 进行自定义排序(按数字的相反数排序)
            // 先转为 Integer 数组,因为 Comparator 不支持基本类型
            Integer[] boxedNumbers = Arrays.stream(numbers).boxed().toArray(Integer[]::new);
            Arrays.sort(boxedNumbers, Comparator.comparingInt(i -> -i));
            // 再转回基本类型数组
            Arrays.setAll(numbers, i -> boxedNumbers[i]);
            System.out.println("按相反数排序后: " + Arrays.toString(numbers));
        }
    }
    // 输出:
    // 排序前: [5, 1, 9, 3, 7, 2, 8, 4, 6]
    // 按相反数排序后: [9, 8, 7, 6, 5, 4, 3, 2, 1]

深入理解:Comparable vs. Comparator

这是一个非常重要的概念,也是面试中的高频考点。

特性 Comparable Comparator
位置 类内部实现 类外部创建,是一个独立的类/函数
目的 定义类的“自然排序”规则 定义“临时/自定义”的排序规则
方法 int compareTo(T o) int compare(T o1, T o2)
使用方式 Arrays.sort(myObjectArray); Arrays.sort(myObjectArray, myComparator);
修改 需要修改源类的代码 不需要修改源类,非常灵活
数量 一个类只能有一个 compareTo 方法 可以创建任意数量的 Comparator 来实现不同排序

选择建议

  • 如果一个类有一个非常明确、主要的排序方式(String 按字典序,Date 按时间),让它实现 Comparable 接口。
  • 如果排序规则是多种多样的、临时的,或者你不想/不能修改源类的代码,那么使用 ComparatorComparator 更加灵活和强大。

性能与注意事项

  1. 时间复杂度:对于大多数情况,Arrays.sort() 的时间复杂度为 O(n log n),这是比较高效的排序算法。

  2. 空间复杂度

    • 基本类型的双轴快速排序是原地排序,空间复杂度为 O(log n)(主要是递归栈空间)。
    • 对象数组的 TimSort 需要额外的临时空间,空间复杂度为 O(n)
  3. 稳定性:对象数组的排序是稳定的,而基本类型数组的排序是不稳定的,在需要稳定排序的场景下,务必使用对象数组。

  4. NullPointerException:如果数组中包含 null 值,调用 Arrays.sort() 会抛出 NullPointerException,在排序前,你需要确保数组中没有 null,或者使用 Comparator 来处理 null 值。

    // 处理 null 值的 Comparator
    Comparator<String> nullSafeComparator = Comparator.nullsFirst(Comparator.naturalOrder());
    String[] arrayWithNull = {"apple", null, "banana", "cherry"};
    Arrays.sort(arrayWithNull, nullSafeComparator);
    // 结果: [null, apple, banana, cherry]

Java 8+ 的新特性:Arrays.parallelSort()

从 Java 8 开始,Arrays 类引入了 parallelSort() 方法,它的行为与 sort() 基本相同,但会利用多核处理器的并行能力来加速排序过程。

  • 适用场景:对于非常大的数组(元素数量超过 10,000),parallelSort() 可能会比 sort() 更快。
  • 实现原理:它会将数组分成多个段,每个段在不同的 CPU 核心上进行排序,最后再将这些已排序的段合并。
  • 小数组:对于小数组,并行排序的开销(线程创建和任务分配)可能比串行排序还要大,因此性能可能更差。
// 语法与 sort() 类似
Arrays.parallelSort(array);
Arrays.parallelSort(array, fromIndex, toIndex);
Arrays.parallelSort(array, comparator);

特性 Arrays.sort()
核心功能 对数组进行原地排序
算法 基本类型: 双轴快速排序 (O(n log n), 不稳定)
对象: TimSort (O(n log n), 稳定)
重载形式 基本类型排序
2. 对象排序 (需 Comparable)
3. 范围排序
4. 自定义排序 (需 Comparator)
关键接口 Comparable (自然排序), Comparator (自定义排序)
现代替代 Arrays.parallelSort() (适用于大数据集,利用多线程)
注意事项 对象数组不能包含 null;基本类型排序不稳定

掌握 Arrays.sort() 的各种用法是 Java 开发者的基本功,理解其背后的原理和 Comparable/Comparator 的区别,能让你写出更灵活、更高效的代码。

分享:
扫描分享到社交APP
上一篇
下一篇