JDK 8 Arrays 详解及详细源码展示

JDK 8 中的 Arrays 类是 java.util 包中的一个工具类,提供了对数组的高效操作方法,包括排序、搜索、填充、复制、比较等。这些方法均为静态方法,无需创建实例即可直接使用,极大简化了数组操作的代码复杂度。

一、Arrays 类的核心功能

功能分类核心方法说明
排序sort(T[] a)sort(T[] a, int fromIndex, int toIndex)sort(byte[] a)对数组进行排序(基本类型用双轴快排,对象用 TimSort)。
搜索binarySearch(T[] a, T key)binarySearch(byte[] a, byte key)二分查找有序数组中的元素(返回索引,未找到返回负数)。
填充fill(T[] a, T val)fill(byte[] a, byte val)用指定值填充数组的所有元素(或指定区间)。
复制copyOf(T[] original, int newLength)copyOfRange(T[] original, int from, int to)复制数组(生成新数组,支持指定长度或区间)。
比较equals(T[] a, T[] a2)equals(byte[] a, byte[] a2)比较两个数组是否相等(内容逐元素比较)。
转换为列表asList(T... a)将数组转换为固定大小的 List(不支持增删操作)。
哈希码hashCode(T[] a)hashCode(byte[] a)计算数组的哈希码(与 List.hashCode() 兼容)。

二、核心方法详解与源码分析

1. 排序方法 sort()

Arrays.sort() 是最常用的方法之一,根据数组类型(基本类型/对象)选择不同的排序算法:

(1)对象数组排序(TimSort)

对于实现了 Comparable 接口的对象数组(如 String[]Integer[]),Arrays.sort() 使用 TimSort 算法(一种稳定的归并排序变种)。TimSort 结合了插入排序和归并排序的优点,对部分有序的数组效率极高(时间复杂度接近 O(n)),最坏情况下为 O(n log n)

源码核心逻辑(简化版)

// 对象数组排序入口(T 需实现 Comparable)
public static <T extends Comparable<? super T>> void sort(T[] a) {
    if (a == null || a.length <= 1) {
        return;
    }
    // 调用 TimSort 实现
    TimSort.sort(a, 0, a.length, null, 0, 0);
}

// TimSort 核心排序方法(简化)
static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c, T[] work, int workBase) {
    // 计算最小运行长度(32~64 之间)
    int minRun = calculateMinRun(hi - lo);
    // 将数组划分为多个“运行”(run,有序的子数组)
    for (int i = lo; i < hi; i += minRun) {
        int end = Math.min(i + minRun - 1, hi - 1);
        insertionSort(a, i, end, c); // 对短于 minRun 的子数组用插入排序
    }
    // 归并所有运行(merge)
    for (int size = minRun; size < hi - lo; size = 2 * size) {
        for (int loRun = lo; loRun < hi; loRun += 2 * size) {
            int hiRun = Math.min(loRun + 2 * size - 1, hi - 1);
            int midRun = loRun + size - 1;
            merge(a, loRun, midRun, hiRun, c, work, workBase);
        }
    }
}
(2)基本类型数组排序(双轴快速排序)

对于基本类型数组(如 int[]double[]),Arrays.sort() 使用 双轴快速排序(Dual-Pivot Quicksort)。该算法通过选择两个枢轴(pivot)将数组划分为三部分(小于左枢轴、介于两枢轴之间、大于右枢轴),减少了递归深度,提升了排序效率(平均时间复杂度 O(n log n),最坏 O(n²),但实际中很少出现)。

源码核心逻辑(以 int[] 为例)

// int 数组排序入口
public static void sort(int[] a) {
    if (a == null || a.length <= 1) {
        return;
    }
    dualPivotQuicksort(a, 0, a.length - 1);
}

// 双轴快速排序核心方法
private static void dualPivotQuicksort(int[] a, int left, int right) {
    // 小数组用插入排序(阈值 47)
    if (right - left < 47) {
        insertionSort(a, left, right);
        return;
    }
    // 选择两个枢轴(左枢轴和右枢轴)
    int pivot1 = a[left], pivot2 = a[right];
    if (pivot1 > pivot2) { // 确保 pivot1 <= pivot2
        swap(a, left, right);
        pivot1 = a[left];
        pivot2 = a[right];
    }
    // 划分区间
    int i = left + 1, j = right - 1, k = left + 1;
    while (k <= j) {
        if (a[k] < pivot1) { // 小于左枢轴
            swap(a, i++, k++);
        } else if (a[k] > pivot2) { // 大于右枢轴
            swap(a, k, j--);
        } else { // 介于两枢轴之间
            k++;
        }
    }
    // 将枢轴放到正确位置
    swap(a, left, --i);
    swap(a, right, ++j);
    // 递归排序三部分
    dualPivotQuicksort(a, left, i - 1);
    dualPivotQuicksort(a, j + 1, right);
    // 处理中间部分(i 到 j)
    dualPivotQuicksort(a, i, j);
}
2. 二分查找 binarySearch()

Arrays.binarySearch() 用于在有序数组中查找指定元素,返回其索引(若未找到返回负数,表示插入位置)。该方法要求数组已排序(升序),否则结果不确定。

源码核心逻辑(以 int[] 为例)

// int 数组二分查找入口
public static int binarySearch(int[] a, int key) {
    return binarySearch0(a, 0, a.length, key);
}

// 二分查找核心方法(左闭右闭区间 [low, high])
private static int binarySearch0(int[] a, int low, int high, int key) {
    while (low <= high) {
        int mid = (low + high) >>> 1; // 防止整数溢出
        int midVal = a[mid];
        if (midVal < key) {
            low = mid + 1;
        } else if (midVal > key) {
            high = mid - 1;
        } else {
            return mid; // 找到元素,返回索引
        }
    }
    return -(low + 1); // 未找到,返回插入位置(-(插入点 + 1))
}
3. 数组复制 copyOf()

Arrays.copyOf() 用于复制数组,生成一个新数组(原数组修改不影响新数组)。支持指定新数组的长度(超出原长度时用默认值填充)。

源码核心逻辑(以 int[] 为例)

// int 数组复制入口
public static int[] copyOf(int[] original, int newLength) {
    return copyOf(original, newLength, int[].class);
}

// 泛型复制方法(使用反射创建新数组)
public static <T> T[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}

// 底层复制逻辑(使用 System.arraycopy)
private static <T, U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    T[] copy = ((Object) newType == (Object) Object[].class) ?
        (T[]) new Object[newLength] :
        (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
    return copy;
}
4. 数组比较 equals()

Arrays.equals() 用于比较两个数组是否相等,要求长度相同且对应位置元素相等null 元素需显式处理)。

源码核心逻辑(以 int[] 为例)

// int 数组比较入口
public static boolean equals(int[] a, int[] a2) {
    if (a == a2) { // 同一对象或都为 null
        return true;
    }
    if (a == null || a2 == null) { // 其中一个为 null
        return false;
    }
    int length = a.length;
    if (a2.length != length) { // 长度不同
        return false;
    }
    for (int i = 0; i < length; i++) { // 逐元素比较
        if (a[i] != a2[i]) {
            return false;
        }
    }
    return true;
}
5. 转换为列表 asList()

Arrays.asList() 将数组转换为 List,但返回的 List固定大小的(基于数组的 AbstractList 实现),不支持 add()remove() 等修改操作(会抛出 UnsupportedOperationException)。

源码核心逻辑

// 可变参数数组转换为 List
@SafeVarargs
public static <T> List<T> asList(T... a) {
    return new ArrayList<>(a); // 内部类 ArrayList(非 java.util.ArrayList)
}

// Arrays 内部的 ArrayList(固定大小)
private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable {
    private final E[] a; // 直接引用原数组

    ArrayList(E[] array) {
        a = Objects.requireNonNull(array);
    }

    // 不支持 add/remove
    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }

    public E remove(int index) {
        throw new UnsupportedOperationException();
    }

    // 其他方法(如 get、size)直接操作原数组
}

三、关键源码细节

1. 内部类 ArrayList(固定大小)

Arrays.asList() 返回的 ListArrays 内部的静态嵌套类,直接持有原数组的引用,因此修改列表元素会反映到原数组(反之亦然)。但由于不支持增删操作,适合需要快速转换且无需修改的场景。

2. 排序算法的选择
  • 对象数组:使用 TimSort(归并排序变种),稳定且对部分有序数据高效。
  • 基本类型数组:使用双轴快速排序,避免了对象排序的额外开销(如比较器调用),更适合数值排序。
3. 哈希码计算 hashCode()

Arrays.hashCode() 计算数组的哈希码,规则与 List.hashCode() 兼容(每个元素的哈希码按顺序组合)。例如,int[] 的哈希码计算为:

public static int hashCode(int[] a) {
    if (a == null) {
        return 0;
    }
    int result = 1;
    for (int element : a) {
        result = 31 * result + element;
    }
    return result;
}

四、使用注意事项

  1. 排序前的有序性binarySearch() 要求数组已排序,否则结果无意义。
  2. 空指针处理Arrays.sort()Arrays.binarySearch() 等方法若传入 null 数组会抛出 NullPointerException
  3. 固定大小列表asList() 返回的 List 不支持增删操作,若需修改,需手动创建新 ArrayList(如 new ArrayList<>(Arrays.asList(arr)))。
  4. 基本类型与包装类:对基本类型数组排序时,Arrays.sort() 直接操作原始数据;对包装类数组(如 Integer[]),排序基于 Comparable 接口。

五、总结

Arrays 类是 JDK 中处理数组的核心工具类,提供了排序、搜索、复制等高频操作的高效实现。其内部根据数据类型(基本类型/对象)选择不同的算法(双轴快排/TimSort),兼顾了性能与易用性。实际开发中,熟练使用 Arrays 类可以显著减少数组操作的代码量,提升开发效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值