首页 / Java 入门教程 / 多维数组

Java 入门教程

多维数组

本教程共 100 篇 · 第 26 篇 · 更新于 2026-08-05 · 约 6 分钟阅读

JavaJava 入门教程二维数组多维数组不规则数组

本节目标:理解二维数组就是”数组的数组”,看清它在内存里的真实样子,并学会处理每行长度不同的不规则数组。

一维数组排成一排。要表示”表格”——比如 3 行 4 列的矩阵——就得用二维数组。很多初学者以为二维数组是块整齐的矩形内存,其实在 Java 里,二维数组本质是”数组里面再装数组”。这个认知对了,后面所有坑都好理解。

声明与初始化

int[][] matrix = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

int[][] 读作”int 的二维数组”。外层每个元素又是一个一维数组。也可以动态创建:

int[][] m = new int[3][4];   // 3 行,每行 4 列,默认全 0

这种一次给定行和列的写法,会同时把每一行都 new 好,用起来最方便。

Note

new int[3][4] 也可以写成 new int[3][] 然后逐行 new。后者能造出每行长度不一样的不规则数组,下面会讲到。

内存里的真实样子

二维数组不是一块整齐的矩形内存。matrix 这个变量指向一个长度为 3 的”行数组”,而行数组的每个元素分别指向各自的一维数组。

matrix ──> [  ref0,  ref1,  ref2  ]
             |      |      |
             v      v      v
            [1,2,3,4] [5,6,7,8] [9,10,11,12]

所以访问元素是 matrix[行][列]

System.out.println(matrix[0][1]);   // 2,第 0 行第 1 列
matrix[1][2] = 99;                  // 改第 1 行第 2 列

理解这个”两层引用”的结构,是看懂不规则数组和遍历越界的前提。

不规则数组

因为”每行是一个独立数组”,每行长度可以不一样。这叫不规则(锯齿)数组

int[][] tri = new int[3][];     // 先定 3 行,列数后补
tri[0] = new int[1];
tri[1] = new int[2];
tri[2] = new int[3];
tri[1][1] = 7;

第一行 1 列、第二行 2 列、第三行 3 列,像三角形。这种灵活度是 C 语言那种”真二维数组”没有的。很多真实数据就是不规则的,比如一个班级里每个学生选的课程数不同。

Tip

动态建不规则数组时,记得先给 tri[i] 各自 new 一个一维数组,否则它还是 null,访问会抛 NullPointerException

遍历二维数组

外层走行,内层走列:

for (int i = 0; i < matrix.length; i++) {          // 行
    for (int j = 0; j < matrix[i].length; j++) {   // 列,用 matrix[i].length
        System.out.print(matrix[i][j] + " ");
    }
    System.out.println();
}

内层长度用 matrix[i].length 而不是固定数,这样不规则数组也能正确遍历。把 matrix[i] 看成一个普通一维数组,二维遍历其实就是”对每一行,做一遍一维遍历”。

Warning

遍历二维数组别写死内层长度(如 j < 4)。一旦遇上不规则数组或行列数变化,就会越界或漏元素。永远用 matrix[i].length,让每行自己决定宽度。

增强 for 也能遍历二维

for (int[] row : matrix) {          // 每次取出一整行(一维数组)
    for (int val : row) {           // 再遍历这一行
        System.out.print(val + " ");
    }
    System.out.println();
}

写法更简洁,同样不会越界。“行”是个一维数组,内层增强 for 自己知道到哪结束。

三维及以上

理论上还能有三维数组 int[][][],表示”数组的数组的数组”,可以想象成”一本书的每一页是一行,每行是一排字”。但实际开发超过二维就很少用,真到了那种复杂度,往往该用对象或集合来表达更清楚了。强行用高维数组,代码会很难读也很难维护。

int[][][] cube = new int[2][3][4];   // 2 层,3 行,4 列

三维数组的遍历就是三层嵌套 for,原理和二维一样,每层用对应维度的 length。

常见错误

  • 忘了给不规则数组的每一行 new,直接访问报空指针。
  • 内层循环写死长度,遇到不规则数组就出错。
  • matrix.length 当成”总元素个数”,其实它只是”行数”。
  • 以为二维数组一定每行等长,其实不是。
Note

二维数组在 Java 里永远是一维数组的数组,没有真正的”矩形内存”。记住这一点,不规则数组、遍历、赋值全部顺理成章。

求二维数组的行数和列数

int rows = matrix.length;           // 行数
int cols = matrix[0].length;        // 列数(假设至少一行)

如果可能没有行,要先判断 rows > 0 再取列数,否则越界。

不规则数组求”总元素数”得累加:

int total = 0;
for (int i = 0; i < tri.length; i++) {
    total += tri[i].length;         // 每行长度不同,逐个加
}

Java 25 LTS 与老写法对照

二维数组的语法从 Java 1.0 起就没变过。但老代码里有些写法现在不推荐了:

写法状态说明
int[][] arr✅ 推荐中括号跟类型
int arr[][]⚠️ 不推荐能跑,但不直观
int[] arr[]⚠️ 不推荐更不直观
new int[3][4]✅ 推荐同时指定行列
new int[3][] 再逐行 new✅ 推荐不规则数组专用

Java 21 引入了 Stream API,也能处理二维数组:

import java.util.Arrays;

int[][] matrix = {{1,2},{3,4}};
// 把二维数组打平成一维流
Arrays.stream(matrix).flatMapToInt(Arrays::stream).forEach(System.out::println);

这是函数式写法,初学了解即可,后面学 Stream 时会展开。

二维数组的适用场景

什么时候用二维数组?

  • 棋盘、矩阵、迷宫等网格数据
  • 像素图片(每个像素一个值)
  • 表格数据(行=记录,列=字段)
  • 动态规划里的状态表
  • 图的邻接矩阵表示

如果数据是”对象”(比如每个格子有多个属性),用对象数组或 List 更合适。二维数组适合”纯数字、结构规整”的场景。

二维数组的浅拷贝陷阱

二维数组的拷贝比一维数组复杂。直接 Arrays.copyOf 只复制外层引用,内层数组还是共享的:

int[][] original = {{1,2},{3,4}};
int[][] copy = Arrays.copyOf(original, original.length);
copy[0][0] = 99;
System.out.println(original[0][0]);   // 99,被改了!

这叫”浅拷贝”。要”深拷贝”得逐行复制:

int[][] deepCopy = new int[original.length][];
for (int i = 0; i < original.length; i++) {
    deepCopy[i] = Arrays.copyOf(original[i], original[i].length);
}

第 27 章会详细讲 Arrays.copyOf 的用法。

速查表

操作写法说明
声明二维int[][] arr;两对中括号
静态初始化{{1,2},{3,4}}每行用花括号
动态初始化new int[3][4]同时指定行列
不规则new int[3][]只指定行,列后补
取行数arr.length外层数组长度
取列数arr[0].length第一行的长度
遍历双层 for内层用 arr[i].length
取总元素数逐行累加不规则数组专用
深拷贝逐行 copyOf浅拷贝会共享内层数组

常见疑问

Q:二维数组一定要每行等长吗? 不一定。Java 的二维数组本质是”数组的数组”,每行独立,可以不等长。只有 new int[r][c] 这种写法才保证等长。

Q:能定义四维或更高维数组吗? 语法上能,int[][][][] 这样的声明合法。但超过二维可读性急剧下降,工程里基本不用。遇到高维数据优先考虑对象建模。

Q:如何复制二维数组? 没有一行搞定的方法。得先复制外层,再逐行 Arrays.copyOf 每个内层数组。这叫”深拷贝”,第 27 章会展开。

Q:二维数组和 List<List> 哪个好? 看场景。数组性能更好、内存更紧凑;List 更灵活、API 更丰富。固定大小的数据用数组,频繁增删的用 List。