定义
Java 的二维数组本质是数组的数组:外层数组的每个元素是一个一维数组的引用。
一维数组: [1, 2, 3]
二维数组: [[1, 2, 3], [4, 5, 6]] // 2行×3列
与 C 语言连续内存模型不同,Java 的二维数组各内层数组在堆中独立分配,因此天然支持不规则数组(每行列数可不同)。
三种定义方式
方式一:静态初始化
已知全部元素时使用:
String[][] scores = {
{"张三", "90", "85"},
{"李四", "88", "92"}
};
int[][] matrix = {
{1, 2, 3},
{4, 5, 6}
};
方式二:动态初始化
先分配内存再逐个赋值:
int[][] arr = new int[3][2]; // 3行2列,元素默认值 0
arr[0][0] = 10;
arr[0][1] = 20;
arr[1][0] = 30;
方式三:不规则数组
只指定行数,每行列数单独定义:
int[][] jagged = new int[2][];
jagged[0] = new int[2]; // 第0行:2列
jagged[1] = new int[3]; // 第1行:3列
jagged[0][0] = 1;
jagged[1][2] = 9;
静态初始化也可定义不规则数组:
int[][] jagged = {
{1, 2},
{3, 4, 5},
{6}
};
访问与修改
通过 数组名[行索引][列索引] 定位元素,索引从 0 开始:
String[][] scores = {
{"张三", "90", "85"},
{"李四", "88", "92"}
};
String math = scores[0][2]; // 取值:"85"
scores[1][1] = "95"; // 赋值
获取行列数:
int rows = scores.length; // 行数:2
int cols = scores[0].length; // 第0行列数:3
不规则数组中每行列数可能不同,取列数必须用 arr[i].length 而非 arr[0].length。
遍历方式
普通 for 循环
需要索引或修改元素时使用:
int[][] matrix = {
{1, 2, 3},
{4, 5, 6}
};
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println();
}
// 输出:
// 1 2 3
// 4 5 6
增强 for 循环
只读取元素时使用:
for (int[] row : matrix) {
for (int num : row) {
System.out.print(num + " ");
}
System.out.println();
}
Stream 流式遍历(JDK 8+)
import java.util.Arrays;
Arrays.stream(matrix)
.forEach(row -> {
Arrays.stream(row)
.forEach(num -> System.out.print(num + " "));
System.out.println();
});
遍历方式对比
|
遍历方式 |
优点 |
缺点 |
适用场景 |
|
普通 for |
有索引,可修改元素 |
代码较长 |
需要定位或修改元素 |
|
增强 for |
代码简洁 |
无索引,不可修改 |
仅读取元素 |
|
Stream |
函数式,可链式过滤映射 |
JDK 8+ 支持 |
需要数据过滤转换 |
不规则数组遍历
内层循环上限必须用当前行的 length:
int[][] jagged = {
{1, 2},
{3, 4, 5}
};
for (int i = 0; i < jagged.length; i++) {
for (int j = 0; j < jagged[i].length; j++) {
System.out.print(jagged[i][j] + " ");
}
System.out.println();
}
// 输出:
// 1 2
// 3 4 5
若改用 jagged[0].length(固定为 2),第 1 行的第三个元素 5 会丢失。
常见操作
计算元素总和
int[][] nums = {{1, 2}, {3, 4}, {5, 6}};
int sum = 0;
for (int[] row : nums) {
for (int num : row) {
sum += num;
}
}
System.out.println(sum); // 21
查找最大值
int[][] nums = {{12, 8}, {9, 25}, {7, 18}};
int max = nums[0][0];
for (int[] row : nums) {
for (int num : row) {
if (num > max) {
max = num;
}
}
}
System.out.println(max); // 25
内存结构
int[][] arr = new int[2][3];
内存布局:
- 栈:变量 arr 存外层数组的地址
- 外层数组(堆):长度 2,每个元素存一个内层数组的地址
- 内层数组 1(堆):长度 3 的 int 数组,元素默认值 0
- 内层数组 2(堆):长度 3 的 int 数组,元素默认值 0
关键结论:Java 二维数组不是连续矩形内存,而是「外层引用数组 + 多个独立一维数组」的组合,因此每行可独立分配不同长度,访问元素需两次寻址。
注意事项
|
问题 |
触发条件 |
异常类型 |
|
索引越界 |
i >= arr.length 或 j >= arr[i].length |
ArrayIndexOutOfBoundsException |
|
空指针 |
定义了行但未初始化:int[][] arr = new int[2][]; arr[0][0] = 1; |
NullPointerException |
|
遍历遗漏 |
不规则数组用 arr[0].length 作为所有行列数上限 |
逻辑错误 |
索引范围示例:
int[][] arr = new int[3][4];
// 行索引:0 ~ 2 (arr.length – 1)
// 列索引:0 ~ 3 (arr[0].length – 1)
// arr[3][0] → ArrayIndexOutOfBoundsException
// arr[0][4] → ArrayIndexOutOfBoundsException
典型应用场景
|
场景 |
示例 |
|
表格数据 |
学生成绩表、商品信息表、员工考勤 |
|
数学矩阵 |
矩阵加法、矩阵乘法、矩阵转置 |
|
游戏棋盘 |
五子棋、扫雷、贪吃蛇 |
|
多层分类 |
省→市→区、大类→小类→商品 |




