1. Arrays类简介
Arrays 类位于 java.util 包下,是一个专门用于操作数组的工具类。由于数组是编程中最基础的数据结构之一,Arrays 提供了大量静态方法来简化数组的常见操作,例如排序、搜索、填充和转换等。
核心特点:
- 该类包含的方法均为 静态方法 ,无需实例化对象,直接通过
Arrays.方法名()调用。
2. Arrays 类的常用方法概览
下表列出了 Arrays 类中最核心、最常用的方法及其功能说明:
| 方法名 | 说明 |
|---|---|
public static String toString(数组) |
将数组的内容拼接成一个格式化的字符串,便于打印查看。 |
public static int binarySearch(数组, 查找的元素) |
使用二分查找法在数组中查找指定元素,返回其索引。 |
public static int[] copyOf(原数组, 新数组长度) |
拷贝数组,可以指定新数组的长度(支持扩容或截断)。 |
public static int[] copyOfRange(原数组, 起始索引, 结束索引) |
拷贝数组的指定范围(左闭右开)。 |
public static void fill(数组, 元素) |
将数组中的所有元素填充为指定的值。 |
public static void sort(数组) |
对数组进行自然排序(默认升序)。 |
public static void sort(数组, 排序规则) |
根据指定的比较器规则对数组进行排序。 |
3. 常用方法详解与演示
3.1 toString 方法:数组转字符串
直接打印数组对象默认输出的是哈希地址,而 toString 方法可以将数组内容转换为易读的字符串格式(例如 [1, 2, 3])。
java
package com.example;
import java.util.Arrays;
public class ArraysDemo {
public static void main(String[] args) {
// toString: 将数组变成字符串
System.out.println("-----------------toString-----------------");
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 直接打印数组对象得到的是地址,使用 Arrays.toString 得到内容
System.out.println(Arrays.toString(arr));
// 输出结果: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
}

3.2 binarySearch 方法:二分查找
二分查找(Binary Search)是一种在有序数组中查找特定元素的高效算法,其时间复杂度为 O(logn)O(\log n)O(logn)。
⚠️ 使用前提:
调用
binarySearch方法前,必须保证数组已经是有序的 (通常是升序)。如果数组未排序,必须先调用Arrays.sort()进行排序,否则查找结果是不确定的。
3.2.1 返回值规则详解:
- 元素存在: 返回该元素在数组中的 真实索引 (≥0\ge 0≥0)。
- 元素不存在: 返回
-(插入点) - 1。- 插入点:指该元素如果要插入数组以保持有序,应该所在的位置索引。
3.2.2 为什么要返回 -(插入点) - 1?
这是为了在返回值上区分"找到了"和"没找到",同时又能提供有用的信息。
- 如果仅仅返回
-插入点,当元素应该插入在索引0时,返回值为-0(即0)。 - 这就与"元素正好在索引
0处被找到"的情况(返回值也是0)产生了冲突。 - 因此,Java 规定返回负数来表示未找到,公式
-(插入点) - 1既保证了结果恒为负数,又可以通过计算反推出该元素应该插入的位置。
java
package com.example;
import java.util.Arrays;
public class ArraysDemo {
public static void main(String[] args) {
// arr 已经是升序数组 {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
System.out.println("-----------------binarySearch-----------------");
// 1. 查找存在的元素:返回真实索引
System.out.println(Arrays.binarySearch(arr, 10)); // 输出: 9 (索引为9)
System.out.println(Arrays.binarySearch(arr, 2)); // 输出: 1 (索引为1)
// 2. 查找不存在的元素:返回 -(插入点) - 1
// 元素 20 比数组中所有元素都大,应该插入在索引 10 的位置
// 计算:-(10) - 1 = -11
System.out.println(Arrays.binarySearch(arr, 20)); // 输出: -11
}
}

3.3 copyOf 与 copyOfRange:数组拷贝
在实际开发中,我们经常需要对数组进行扩容 或截取 。Arrays 提供了两个非常便捷的方法来实现这一功能。
3.3.1 copyOf 方法:全量复制与扩容
Arrays.copyOf 方法主要用于复制整个数组。它允许我们指定新数组的长度,从而轻松实现数组的扩容 或截断。
💡 核心机制:
- 扩容 :如果指定的
newLength大于原数组长度,新数组多出的部分会自动填充该数据类型的默认初始化值 (例如int类型为0,引用类型为null)。- 截断 :如果指定的
newLength小于原数组长度,新数组将只保留原数组的前newLength个元素,多余部分被丢弃。
java
package com.example;
import java.util.Arrays;
public class ArraysDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
System.out.println("--------------------copyOf--------------------");
// 场景:数组扩容
// 将数组长度从 10 扩展到 20
int[] newArr1 = Arrays.copyOf(arr, 20);
// 输出结果:原数据保留,后10位自动补0
System.out.println(Arrays.toString(newArr1));
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
// 场景:数组截断
// 将数组长度从 10 截断为 5
int[] newArr2 = Arrays.copyOf(arr, 5);
// 输出结果:只保留前 5 个元素
System.out.println(Arrays.toString(newArr2));
// [1, 2, 3, 4, 5]
}
}

3.3.2 copyOfRange 方法:指定范围拷贝
如果你只需要数组中的某一部分数据,Arrays.copyOfRange 是最佳选择。它允许你通过指定索引范围来提取数组的片段。
⚠️ 关键细节:
- 区间规则 :遵循 "左闭右开" 原则,即
[from, to)。- 包含 :起始索引
from对应的元素。- 不包含 :结束索引
to对应的元素(即复制到to - 1为止)。
java
package com.example;
import java.util.Arrays;
public class ArraysDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
System.out.println("--------------------copyOfRange--------------------");
// 场景:截取数组片段
// 截取索引 0 到 9 的元素(不包含索引 9 的元素)
int[] newArr2 = Arrays.copyOfRange(arr, 0, 9);
// 输出结果:只包含索引 0~8 的元素
System.out.println(Arrays.toString(newArr2));
// [1, 2, 3, 4, 5, 6, 7, 8, 9]
}
}

3.4 fill 方法:填充数组
如果需要将数组中的所有元素快速替换为同一个值,可以使用 fill 方法。
⚠️ 注意: 该操作是直接修改原数组的内容,没有返回值(void)。
java
package com.example;
import java.util.Arrays;
public class ArraysDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// fill: 填充数组
System.out.println("--------------------fill--------------------");
// 将 arr 数组中的所有元素都填充为 100
Arrays.fill(arr, 100);
System.out.println(Arrays.toString(arr));
// 输出结果: [100, 100, 100, 100, 100, 100, 100, 100, 100, 100]
}
}

3.5 sort 方法:数组排序
Arrays.sort 是 Java 中最常用的排序工具。根据数据类型的不同,它提供了基本类型排序 和对象类型定制排序两种模式。
3.5.1 基本类型排序(自然排序)
对于 int、double、char 等基本数据类型的数组,直接调用 sort(array) 即可。
- 规则 :按照数值的升序(从小到大)排列。
- 底层算法:使用的是经过优化的快速排序算法(Dual-Pivot Quicksort),性能极高。
java
package com.example;
import java.util.Arrays;
public class ArraysDemo {
public static void main(String[] args) {
System.out.println("-----------------Sort Basic-----------------");
int[] arr = {10, 2, 3, 5, 6, 1, 7, 8, 4, 9};
// 直接排序,默认升序
Arrays.sort(arr);
// 输出结果: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
System.out.println(Arrays.toString(arr));
}
}

3.5.2 对象类型排序(定制排序)
对于对象数组(如 Integer[]、String[] 或自定义对象),如果需要降序 或特殊规则 排序,则需要传入第二个参数:Comparator(比较器)。
⚠️ 重要提示:
基本数据类型(如
int[])不支持 Comparator 排序。如果要使用比较器,必须使用对应的包装类数组(如Integer[])。
3.5.2.1 核心逻辑:compare 方法的返回值
Comparator 接口中的 compare(T o1, T o2) 方法决定了排序的顺序。你可以将其理解为 o1 与 o2 的"PK"规则,返回值决定了它们的位置关系:
| 返回值 | 含义 | 结果 |
|---|---|---|
| 负数 (< 0) | o1 小于 o2 |
o1 排在 o2 前面(不交换) |
| 正数 (> 0) | o1 大于 o2 |
o1 排在 o2 后面(交换位置) |
| 零 (= 0) | o1 等于 o2 |
位置不变 |
如何实现升序与降序?
- 升序(小 -> 大): 使用
o1 - o2- 如果
o1比o2大,结果为正数,o1会被扔到后面去。
- 如果
- 降序(大 -> 小): 使用
o2 - o1- 如果
o1比o2大,结果为负数,o1会留在前面。
- 如果
3.5.2.2 代码演示:匿名内部类(传统写法)
java
package com.example;
import java.util.Arrays;
import java.util.Comparator;
public class ArraysDemo {
public static void main(String[] args) {
System.out.println("-----------------Sort Descending-----------------");
// 注意:必须使用包装类 Integer[],不能用 int[]
Integer[] arrObj = {2, -3, 1, 5, 6, 7, 6, 4, 9};
// 需求:降序排列 (大 -> 小)
Arrays.sort(arrObj, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// o2 - o1 表示降序
return o2 - o1;
}
});
// 输出结果: [9, 7, 6, 6, 5, 4, 2, 1, -3]
System.out.println(Arrays.toString(arrObj));
}
}

3.5.2.3 代码演示:Lambda 表达式(Java 8+ 推荐写法)
Lambda 表达式可以让代码更加简洁。
java
package com.example;
import java.util.Arrays;
import java.util.Comparator;
public class ArraysDemo {
public static void main(String[] args) {
System.out.println("-----------------Sort Descending-----------------");
// 注意:必须使用包装类 Integer[],不能用 int[]
Integer[] arrObj = {2, -3, 1, 5, 6, 7, 6, 4, 9};
// 需求:升序排列 (大 -> 小)
//lambda 表达式 简化 匿名内部类
Arrays.sort(arrObj, (o1, o2) -> o2 - o1);
// 输出结果: [9, 7, 6, 6, 5, 4, 2, 1, -3]
System.out.println(Arrays.toString(arrObj));
}
}

3.5.3 进阶技巧:规避整数溢出风险
在上一节中,我们使用了 o2 - o1 的方式进行降序排序 。虽然写法简单,但在处理极端数值时,这种写法存在致命缺陷------整数溢出(Integer Overflow)。
为什么减法运算存在隐患?
Java 中 int 的取值范围约为 -21亿 到 21亿。
- 场景模拟 :假设
o2是一个很大的正数(如20亿),o1是一个绝对值很大的负数(如-20亿)。 - 数学计算 :20亿−(−20亿)=40亿20亿 - (-20亿) = 40亿20亿−(−20亿)=40亿。
- 实际结果 :
int类型无法存储 40亿,数值会发生溢出 ,结果会变成一个负数。 - 致命后果 :排序算法会误以为
o1小于o2,导致排序逻辑完全混乱。
官方推荐的稳健写法
为了解决这个问题,Java 官方推荐使用 Integer.compare(x, y) 方法。它内部采用逻辑判断(If-Else)而非数值相减,彻底规避了溢出风险,是生产环境中的标准写法。
| 排序需求 | 减法写法 (不推荐) | 官方标准写法 (推荐) | 逻辑含义 |
|---|---|---|---|
| 升序 (从小到大) | o1 - o2 |
Integer.compare(o1, o2) |
o1 与 o2 比较 |
| 降序 (从大到小) | o2 - o1 |
Integer.compare(o2, o1) |
o2 与 o1 比较 |
💡 核心口诀:
- 升序 :
compare(o1, o2)- 降序 :
compare(o2, o1)
代码演示:降序场景下的对比
为了验证效果,我们构造一组包含极大值和极小值的数据进行降序排序测试。
java
package com.example;
import java.util.Arrays;
public class ArraysDemo {
public static void main(String[] args) {
System.out.println("-----------------Compare Demo-----------------");
// 构造极端数据:包含最大正值和最小负值,极易触发溢出
Integer[] arr = {1, -300, -2000000000, 2000000000};
// 1. 【错误示范】使用减法实现降序
// 风险点:当计算 (-20亿) - (20亿) 时,结果 -40亿 会溢出变为正数
Arrays.sort(arr, (o1, o2) -> o2 - o1);
System.out.println("减法排序结果 (潜在风险): " + Arrays.toString(arr));
// 重置数据
arr = new Integer[]{1, -300, -2000000000, 2000000000};
// 2. 【正确示范】使用 Integer.compare 实现降序
// 优势:内部逻辑判断,绝对安全,结果准确
Arrays.sort(arr, (o1, o2) -> Integer.compare(o2, o1));
System.out.println("compare排序结果 (安全稳健): " + Arrays.toString(arr));
}
}
