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()
返回的 List
是 Arrays
内部的静态嵌套类,直接持有原数组的引用,因此修改列表元素会反映到原数组(反之亦然)。但由于不支持增删操作,适合需要快速转换且无需修改的场景。
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;
}
四、使用注意事项
- 排序前的有序性:
binarySearch()
要求数组已排序,否则结果无意义。 - 空指针处理:
Arrays.sort()
、Arrays.binarySearch()
等方法若传入null
数组会抛出NullPointerException
。 - 固定大小列表:
asList()
返回的List
不支持增删操作,若需修改,需手动创建新ArrayList
(如new ArrayList<>(Arrays.asList(arr))
)。 - 基本类型与包装类:对基本类型数组排序时,
Arrays.sort()
直接操作原始数据;对包装类数组(如Integer[]
),排序基于Comparable
接口。
五、总结
Arrays
类是 JDK 中处理数组的核心工具类,提供了排序、搜索、复制等高频操作的高效实现。其内部根据数据类型(基本类型/对象)选择不同的算法(双轴快排/TimSort),兼顾了性能与易用性。实际开发中,熟练使用 Arrays
类可以显著减少数组操作的代码量,提升开发效率。