Java数组基础与声明
数组声明与初始化
在<Java中,数组是一种引用数据类型,用于存放同一类型的元素。要声明一个数组,需要指明元素类型并带上方括号,随后通过 new 关键字分配长度。常见的写法包括:
int[] nums = new int[5];
对于局部变量,未显式初始化的基本类型数组元素将被设为默认初始值,如 0、false 等,引用类型为 null。
boolean[] flags = new boolean[3]; // 默认 false
String[] names = new String[2]; // 默认 null
数组的索引从 0 开始,访问最大索引为 length-1,若越界将抛出 ArrayIndexOutOfBoundsException,这是在 算法与数据结构中需要特别注意的点。
int first = nums[0];
数组类型与默认值
数组分为两大类:基本数据类型数组与 引用类型数组。基本类型数组的元素会直接存储数值,而 引用类型数组的元素存储对象的引用。理解这一点对于避免内存泄漏和空指针异常至关重要。
int[] primitiveArr = {1, 2, 3};
Object[] objArr = new Object[2];
objArr[0] = new Object();
在实际编程中,固定长度的数组常用于底层算法、哈希表的实现以及性能敏感的场景,而需要可变容量的场景通常会结合 ArrayList 等集合类。
算法中的数组操作全解析
遍历、拷贝与替换
在算法实现中,遍历是最基础的数组操作之一。常见的遍历方式包括传统的 下标遍历 与增强型 for 循环。对数组执行拷贝、替换等操作时,常用的工具类方法有 Arrays.copyOf 与 System.arraycopy,它们在性能与简洁性之间提供了良好的权衡。
// 下标遍历
for (int i = 0; i < arr.length; i++) {
// 处理 arr[i]
}
// 增强型 for 循环
for (int v : arr) {
// 处理 v
}
数组拷贝的常见用法:
import java.util.Arrays;
int[] copy1 = Arrays.copyOf(arr, arr.length); // 使用 Arrays.copyOf 拷贝
int[] dest = new int[arr.length];
System.arraycopy(arr, 0, dest, 0, arr.length); // 使用 System.arraycopy 拷贝
排序与查找的数组实现
排序是将数据转化为有序结构的关键步骤,常用的 API 为 Arrays.sort,时间复杂度通常为 O(n log n)。查找则有两种常用方式:线性查找与二分查找,二分查找的前提是数组有序,复杂度为 O(log n)。
int[] a = {5, 2, 9, 1};
Arrays.sort(a); // [1, 2, 5, 9]
int idx = Arrays.binarySearch(a, 5); // 2
常用模式:滑动窗口、前缀和等
二分查找在数组上的实现
手写实现的二分查找同样高效,核心在于通过中间下标不断缩小查找区间。以下实现给出一个通用版本,时间复杂度为 O(log n),适用于有序数组。
public static int binarySearch(int[] a, int target) {
int left = 0;
int right = a.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (a[mid] == target) return mid;
if (a[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}
滑动窗口与前缀和技巧
滑动窗口通过维护一个动态区间来解决子数组相关问题,常见于长度约束、和、最大值等场景。下列模式演示了一个简单的“保持和不超过 k”的滑动窗口:
int left = 0, sum = 0;
for (int right = 0; right < arr.length; right++) {
sum += arr[right];
while (sum > k) {
sum -= arr[left++];
}
// 窗口大小为 right - left + 1
}
前缀和通过构造一个额外的数组 pre,它表示前缀和。利用前缀和可以在 O(1) 时间内计算任意子区间的和:pre[j] - pre[i]。
int[] pre = new int[arr.length + 1];
for (int i = 0; i < arr.length; i++) pre[i + 1] = pre[i] + arr[i];
int sumSub = pre[j] - pre[i];
性能与注意事项
内存布局与缓存友好
在 Java 中,数组是连续内存布局的对象,因此在处理大规模数据时,缓存命中率 与 内存带宽 会显著影响性能。对算法来说,尽量减少随机访问、连续访问的模式可以获得更好的缓存局部性,提升吞吐量。
// 访问模式尽量线性扫描,以提高缓存命中率
for (int i = 0; i < arr.length; i++) {
process(arr[i]);
}
边界检查、空指针与异常处理
在进行数组操作时,必须始终关注边界条件与空指针风险。越界访问、空指针和未初始化的数组都可能引发运行时异常,需要在循环、分支和方法参数处进行必要的校验。
if (arr != null && arr.length > 0) {
for (int i = 0; i < arr.length; i++) {
// 安全处理
}
}


