Java数组操作终极指南:从入门到实战

概述

数组是Java中最基础、最常用的数据结构之一。它是一种固定长度相同类型元素的连续存储结构。无论是存储基本类型还是对象引用,数组都能高效地完成数据批量处理。本文将从零开始,系统梳理Java数组的声明、初始化、遍历、复制、排序、查找、转换等所有核心操作,并给出实战场景和避坑指南。文中图表使用 Mermaid 语法绘制,可在 VuePress 等支持 Mermaid 的 Markdown 渲染器中正常显示。

核心概念

1. 声明与创建

// 方式一:先声明,再分配空间
int[] arr1;
arr1 = new int[5];

// 方式二:声明并分配空间
int[] arr2 = new int[5];

// 方式三:静态初始化(直接指定元素)
int[] arr3 = {1, 2, 3, 4, 5};

// 方式四:匿名数组(常用于方法参数)
printArray(new int[]{10, 20, 30});

注意int[] arr = new int[5] 会分配5个int类型的空间,每个元素默认值为0。

2. 访问与修改

通过下标索引(从0开始)访问或修改元素:

int[] scores = {85, 92, 78};
System.out.println(scores[1]);   // 92
scores[0] = 90;

3. 数组长度

每个数组都有一个 length 属性(注意不是方法):

int[] arr = new int[10];
int len = arr.length;   // 10

4. 遍历方式

4.1 普通for循环

for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}

4.2 增强for循环(forEach)

for (int value : arr) {
    System.out.println(value);
}

5. 多维数组

Java中多维数组本质是“数组的数组”。

// 声明二维数组
int[][] matrix = new int[3][4];

// 静态初始化
int[][] grid = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

// 不规则数组(每行长度可以不同)
int[][] ragged = new int[3][];
ragged[0] = new int[2];
ragged[1] = new int[3];
ragged[2] = new int[1];

遍历二维数组:

for (int i = 0; i < grid.length; i++) {
    for (int j = 0; j < grid[i].length; j++) {
        System.out.print(grid[i][j] + " ");
    }
    System.out.println();
}

实战操作

1. 数组复制

1.1 使用 `System.arraycopy()`

底层原生方法,效率最高:

int[] src = {1, 2, 3, 4, 5};
int[] dest = new int[5];
System.arraycopy(src, 0, dest, 0, src.length);
// dest = {1, 2, 3, 4, 5}

1.2 使用 `Arrays.copyOf()`

int[] copy = Arrays.copyOf(src, src.length);
// 还可指定新长度,多余补默认值
int[] shortCopy = Arrays.copyOf(src, 3);  // {1,2,3}

1.3 使用 `clone()`

int[] cloneArr = src.clone();

注意clone() 对一维基本类型数组是深拷贝,对对象数组是浅拷贝。

2. 数组排序

2.1 基本类型排序(升序)

int[] nums = {3, 1, 4, 1, 5, 9};
Arrays.sort(nums);
// nums = {1, 1, 3, 4, 5, 9}

2.2 对象排序(需实现 Comparable 或提供 Comparator)

String[] names = {"Tom", "Alice", "Bob"};
Arrays.sort(names);   // 按字典序:"Alice", "Bob", "Tom"

// 自定义排序(降序)
Arrays.sort(names, (a, b) -> b.compareTo(a));

2.3 部分排序(指定范围)

int[] arr = {3, 2, 1, 4, 5};
Arrays.sort(arr, 0, 3);   // 只对前3个排序:{1,2,3,4,5}

3. 二分查找

前提:数组必须已排序。

int[] nums = {1, 3, 5, 7, 9};
int index = Arrays.binarySearch(nums, 5);   // 返回2
int notFound = Arrays.binarySearch(nums, 4); // 返回负数(-(插入点)-1)

4. 填充与比较

int[] arr = new int[5];
Arrays.fill(arr, 100);   // arr = {100,100,100,100,100}

// 数组比较(元素逐个比较)
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println(Arrays.equals(a, b)); // true

// 深层比较(用于多维数组)
int[][] deepA = {{1},{2}};
int[][] deepB = {{1},{2}};
System.out.println(Arrays.deepEquals(deepA, deepB)); // true

5. 转 List

String[] arr = {"a", "b", "c"};
List<String> list = Arrays.asList(arr);
// 注意:返回的 List 大小固定,不能 add/remove,但可以 set

若要可变 List:

List<String> mutableList = new ArrayList<>(Arrays.asList(arr));

6. 流操作(Java 8+)

利用 Arrays.stream() 可以方便地进行过滤、映射、归约等操作:

int[] nums = {1, 2, 3, 4, 5};

// 求和
int sum = Arrays.stream(nums).sum();

// 筛选偶数
int[] evens = Arrays.stream(nums).filter(n -> n % 2 == 0).toArray();

// 映射为字符串
String[] strs = Arrays.stream(nums)
                     .mapToObj(String::valueOf)
                     .toArray(String[]::new);

7. 并行操作(并行排序、并行前缀)

int[] data = {5, 3, 1, 4, 2};
Arrays.parallelSort(data);   // 多线程排序(大数据量时性能优)

// 并行前缀(累加)
int[] prefix = {1, 2, 3, 4};
Arrays.parallelPrefix(prefix, (x, y) -> x + y);
// prefix = {1, 3, 6, 10}

8. 数组转为字符串

int[] arr = {1, 2, 3};
System.out.println(Arrays.toString(arr));  // [1, 2, 3]

// 多维数组
int[][] matrix = {{1,2},{3,4}};
System.out.println(Arrays.deepToString(matrix)); // [[1, 2], [3, 4]]

9. 比较与哈希

int[] a = {1, 2};
int[] b = {1, 2};
System.out.println(a.equals(b));    // false(比较的是引用)
System.out.println(Arrays.equals(a, b)); // true

// 计算哈希码(常用于集合)
int hash = Arrays.hashCode(a);

10. 实战:数组去重

public static int[] removeDuplicates(int[] nums) {
    return Arrays.stream(nums)
                 .distinct()
                 .toArray();
}

11. 实战:数组合并

int[] first = {1, 2};
int[] second = {3, 4};
int[] combined = new int[first.length + second.length];
System.arraycopy(first, 0, combined, 0, first.length);
System.arraycopy(second, 0, combined, first.length, second.length);
// or use Stream
int[] merged = IntStream.concat(Arrays.stream(first), Arrays.stream(second)).toArray();

图表与流程解析

以下使用 Mermaid 绘制数组操作的核心流程图,展示从创建到最终输出结果的典型路径:

flowchart TD A[声明数组] --> B{初始化方式?} B -->|静态| C[直接赋值元素] B -->|动态| D[new 类型[长度]] C --> E[获取长度 .length] D --> E E --> F[遍历/修改元素] F --> G{需要复制?} G -->|是| H[System.arraycopy / Arrays.copyOf / clone] G -->|否| I{需要排序?} I -->|是| J[Arrays.sort / parallelSort] I -->|否| K[执行其他操作: 查找/转换/流处理] J --> K H --> K K --> L[输出结果 Arrays.toString]

此外,二维数组的内存布局可表示为:

graph LR subgraph 二维数组引用 A[matrix] --> B[第0行引用] A --> C[第1行引用] A --> D[第2行引用] end subgraph 实际一维数组 B --> E[0,1,2] C --> F[3,4,5] D --> G[6,7,8] end

注意事项(避坑指南)

  1. 数组越界:下标范围 0 ~ length-1,访问 arr[length] 会抛出 ArrayIndexOutOfBoundsException
  2. 默认值:基本类型数组元素有默认值(int:0, double:0.0, boolean:false),引用类型数组默认值为 null
  3. 对象数组是引用Object[] objs = new Object[3] 中存的是引用,修改引用对象的属性会影响所有指向该对象的元素。
  4. 静态初始化不能拆分int[] arr; arr = {1,2,3}; 非法,必须写成 arr = new int[]{1,2,3};
  5. length 是属性不是方法:不要写成 arr.length()
  6. Arrays.asList() 的坑:返回的 List 由原数组支持,大小固定,不能 add/remove;且如果数组是基本类型,asList() 会将整个数组作为单个元素返回,应使用 IntStream
  7. 浅拷贝与深拷贝clone()System.arraycopy() 对基本类型数组是深拷贝,对对象数组是浅拷贝(只复制引用)。需要深拷贝对象数组时需手动遍历或使用序列化。
  8. 并行排序的性能parallelSort() 适用于大规模数据(>几千),小数组使用 sort() 更快。
  9. 不要使用 == 比较数组内容== 比较的是引用地址,应使用 Arrays.equals()

总结

Java 数组虽然简单,但背后涉及声明、初始化、赋值、遍历、复制、排序、查找、转换、流式操作等一系列实用技巧。理解这些核心操作可以让你在日常编码中高效处理批量数据。建议记住以下常用 API:

操作 关键方法
复制 System.arraycopy(), Arrays.copyOf(), clone()
排序 Arrays.sort(), Arrays.parallelSort()
查找 Arrays.binarySearch()
填充 Arrays.fill()
比较 Arrays.equals(), Arrays.deepEquals()
转字符串 Arrays.toString(), Arrays.deepToString()
转List Arrays.asList()
流处理 Arrays.stream()

掌握这些内容后,你就能自信地说:“数组操作,我全栈了!”


本文使用 Mermaid 图表辅助说明,在支持 Mermaid 的 Markdown 编辑器(如 VuePress、Typora、GitHub)中可正常渲染。