Java多维数组的声明与操作语法

Java中多维数组本质是“数组的数组”,声明推荐int[][] arr,禁用int3 arr;初始化需分步new,支持不规则数组,静态初始化仅限声明时。

Java中多维数组的声明方式区别于C/C++

Java没有真正意义上的“多维数组”,只有“数组的数组”。这意味着 int[][] 实际上是一个 int[] 类型的引用数组,每个元素又指向一个一维 int[]。声明时方括号位置影响语义:int[][] arr 合法,int[] arr[] 虽然语法允许(为兼容C风格),但不推荐;int[] arr[] 容易让人误以为是“数组+指针”混合结构,实际仍是引用数组。

常见错误:写成 int[3][4] arr —— 这在Java中非法,维度大小不能出现在声明阶段。

  • int[][] matrix:推荐,清晰表达“二维引用数组”
  • int[] matrix[]:可编译,但语义模糊,IDE通常会警告
  • int matrix[][]:编译失败,方括号不能紧贴变量名

初始化必须分两步:分配外层数组 + 分配内层数组

Java不支持像C那样用 {{1,2},{3,4}} 一步完成堆内存分配(除非在声明同时使用静态初始化器)。运行时创建需显式调用 new 两次:

int[][] grid = new int[2][];
grid[0] = new int[3]; // 第一行长度为3
grid[1] = new int[5]; // 第二行长度为5(可不等长)

这种“不规则数组”(jagged array)是合法且常见的。若要规则矩形结构,可简写为:new int[2][3],此时JVM自动为每行分配长度为3的一维数组。

  • 静态初始化器只能用于声明时:int[][] a = {{1,2}, {3,4,5}};
  • new int[2][3] 等价于先 new int[2][] 再循环 new int[3]
  • 忘记给某行赋值(如只做 new int[2][] 就访问 grid[0][0])会触发 NullPointerException

遍历与索引访问要注意空指针和越界风险

由于每行长度可能不同,用 for (int i = 0; i 遍历外层后,内层必须检查 arr[i] != null 并用 arr[i].length 获取当前行长度,不能直接套用固定列数。

for (int i = 0; i < matrix.length; i++) {
    if (matrix[i] != null) {
        for (int j = 0; j < matrix[i].length; j++) {
            System.out.print(matrix[i][j] + " ");
        }
    }
    System.out.println();
}
  • 访问 matrix[i][j] 前,必须确保 i 且 j
  • matrix[i] 可能为 null,尤其在手动分配不完整时
  • 增强for循环(for (int[] row : matrix))可避免外层索引

    错误,但无法获知当前行号

性能与内存布局:每行独立分配,缓存不友好

Java多维数组在内存中不是连续块。new int[1000][1000] 会先分配1000个引用,再分配1000个长度为1000的 int[] 对象,共1001个对象。这带来两重开销:GC压力增大、CPU缓存命中率低(行间跳转导致cache line失效)。

  • 对性能敏感场景(如数值计算),优先考虑一维数组模拟二维:int[] flat = new int[rows * cols],用 flat[i * cols + j] 访问
  • ArrayList 不能替代 int[][] —— 泛型擦除后存储的是 Object[],装箱/拆箱开销大
  • 使用 Arrays.deepToString() 打印调试,比手写嵌套循环快且安全

最易被忽略的是:即使声明为 int[][],只要某行未初始化,它就是 null;而C语言里类似声明必有确定内存布局,这点差异常导致NPE排查困难。