在Java开发中,数组是最基础的数据容器之一。虽然数组本身只是连续的内存空间,但JDK提供的java.util.Arrays工具类封装了大量静态方法,让排序、查找、复制、比较等操作变得非常简单。这个类从Java 1.2就存在,并且随着版本迭代不断增加新能力,比如Java 8引入的并行排序。理解Arrays的常用方法,能显著提升代码可读性和执行效率。

Arrays类的基础定位
Arrays是一个工具类,构造方法被私有化,因此不能创建实例,所有方法通过类名直接调用。它主要面向数组提供批处理操作,避免了开发者重复编写循环逻辑。从底层看,很多方法如排序对于基本类型采用了双轴快速排序(Dual-Pivot Quicksort),对于对象类型在Java 7之后使用了TimSort,这些算法都经过高度优化。
需要注意的是,Arrays操作的是数组对象本身(部分方法如copyOf返回新数组),并且多数方法对null数组会抛出NullPointerException。在泛型与自动装箱场景下,对象数组和基本类型数组的处理存在区别,例如Arrays.equals(int[], int[])与Arrays.equals(Object[], Object[])是两套重载逻辑。
数组排序:sort与parallelSort
最常用的是Arrays.sort方法。对于基本类型数组,它原地排序,不返回新数组;对于对象数组,可以传入Comparator来自定义顺序。下面的例子展示了对整型数组排序以及按字符串长度排序对象数组:
import java.util.Arrays;
import java.util.Comparator;
public class SortDemo {
public static void main(String[] args) {
int[] nums = {5, 2, 9, 1, 3};
Arrays.sort(nums);
System.out.println(Arrays.toString(nums)); // [1, 2, 3, 5, 9]
String[] words = {"apple", "pi", "banana", "kiwi"};
// 按字符串长度升序排列
Arrays.sort(words, Comparator.comparingInt(String::length));
System.out.println(Arrays.toString(words)); // [pi, kiwi, apple, banana]
}
}
从Java 8开始,Arrays.parallelSort可利用多核并行排序,在数据量较大时更有优势,但小数组可能因线程调度反而变慢。如果只需要部分排序,可使用Arrays.sort(array, fromIndex, toIndex)指定区间,这在大数组中处理局部数据时非常实用。
使用排序方法时要注意,如果对象数组中的元素没有实现Comparable且未提供Comparator,运行时会抛出ClassCastException。另外排序是稳定的(对象数组的sort对于相等元素保留原有顺序),这一点在多次排序组合业务字段时很重要。
二分查找:binarySearch的正确姿势
Arrays.binarySearch用于在已排序数组中查找元素,返回索引;若未找到则返回负数插入点。关键是数组必须提前排好序,否则结果无意义。下面的代码演示了基本使用和未排序导致的错误:
import java.util.Arrays;
public class SearchDemo {
public static void main(String[] args) {
int[] data = {1, 3, 5, 7, 9};
int idx = Arrays.binarySearch(data, 5);
System.out.println("找到索引:" + idx); // 2
int[] unsorted = {3, 1, 5};
// 错误示范:未排序直接二分查找
int bad = Arrays.binarySearch(unsorted, 1);
System.out.println("未排序查找结果:" + bad); // 可能为负数或错误正数
}
}
对于对象数组,binarySearch同样支持Comparator参数,且必须与排序时使用的比较规则一致,否则会搜不到正确位置。当数组中存在重复元素时,二分查找不保证返回哪一个匹配项的索引,若需精确控制应使用边界查找自行实现。
实际项目中,如果数组频繁变动,维护排序状态成本较高,此时应考虑使用TreeSet或List配合Collections.sort,而不是每次都重排再加二分。Arrays.binarySearch更适合一次性构建静态字典或批量处理中间结果的场景。
填充、复制与比较
Arrays.fill能把数组所有元素或某段区间设为指定值,非常适合初始化。Arrays.copyOf和copyOfRange用于扩容或截取,内部调用System.arraycopy效率很高。Arrays.equals与deepEquals则用于内容比对,后者能递归比较嵌套数组。
import java.util.Arrays;
public class MiscDemo {
public static void main(String[] args) {
int[] buf = new int[5];
Arrays.fill(buf, -1);
System.out.println(Arrays.toString(buf)); // [-1, -1, -1, -1, -1]
int[] copy = Arrays.copyOf(buf, 8); // 扩容到8,多出的元素为0
System.out.println(Arrays.toString(copy));
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println("是否相等:" + Arrays.equals(a, b)); // true
}
}
在多维数组场景中,equals只比较第一层引用,而deepEquals会深入比较每一个子数组,因此二维以上的数组比对必须使用deepEquals。复制操作生成的是新数组,修改副本不会影响原数组,这点与直接赋值引用完全不同。
填充和复制方法虽然简单,但在构建缓存、重置状态、批量生成测试数据时非常高效。结合sort和binarySearch,就能完成一套完整的数组生命周期管理,无需引入额外第三方库。
转成字符串与流式协作
调试时常用Arrays.toString或Arrays.deepToString把数组打印出来,避免直接打印对象引用得到无意义的哈希值。Java 8之后,还可以通过Arrays.stream将数组转为流,从而使用函数式操作。
import java.util.Arrays;
public class StreamDemo {
public static void main(String[] args) {
int[] values = {4, 2, 8, 1};
int sum = Arrays.stream(values).sum();
System.out.println("总和:" + sum);
long count = Arrays.stream(values).filter(v -> v > 2).count();
System.out.println("大于2的个数:" + count);
}
}
将数组接入Stream后,可以无缝使用map、filter、reduce等操作,代码更声明式。不过基本类型流(IntStream等)和对象流(Stream<T>)在装箱拆箱上有性能差异,处理超大数组时应关注这一点。
总体而言,Arrays工具类覆盖了绝大多数数组处理需求。熟悉它的方法签名和底层约定,比如排序稳定性、二分前提、深浅比较区别,能让我们写出更简洁且不易出错的Java代码。