8人参与 • 2026-01-31 • Java
在 java 中,“多重数组”通常指的是多维数组(multidimensional arrays),最常见的是二维数组(如矩阵),但也可以有三维、四维等。
java 的多重数组本质上是 “数组的数组”(array of arrays),即:
int[] arrint[][] matrix → 每个元素是一个 int[]int[][][] cube → 每个元素是一个 int[][]java 的多维数组不要求每行长度相同(称为“锯齿数组”或“不规则数组”)。
int[][] matrix; // 声明一个二维整型数组 string[][][] data; // 三维字符串数组
int[][] grid = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int rows = 3, cols = 4; int[][] mat = new int[rows][cols]; // 3x4 的全 0 矩阵
int[][] jagged = new int[3][]; // 3 行,列数未定 jagged[0] = new int[2]; // 第0行有2列 jagged[1] = new int[5]; // 第1行有5列 jagged[2] = new int[1]; // 第2行有1列
注意:new int[3][4] 是规则数组;new int[3][] 是锯齿数组(需手动初始化每行)。
int value = matrix[i][j]; // 读取第 i 行第 j 列 matrix[i][j] = 100; // 修改
边界检查:java 会在运行时自动检查下标是否越界(抛出 arrayindexoutofboundsexception)。
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();
}
matrix.length → 行数matrix[i].length → 第 i 行的列数(支持锯齿数组!)for (int[] row : matrix) {
for (int val : row) {
system.out.print(val + " ");
}
system.out.println();
}
推荐:优先使用 for-each,避免下标错误,尤其处理锯齿数组时更安全。
java 的二维数组不是一块连续内存(不像 c/c++),而是:
matrix → [ ref0, ref1, ref2 ]
↓ ↓ ↓
[1,2] [3,4,5] [6]
优势:灵活;劣势:缓存局部性较差(相比连续内存)
| 场景 | 示例 |
|---|---|
| 矩阵操作 | 旋转矩阵、螺旋遍历、对角线遍历 |
| 动态规划 | 二维 dp 表(如 lcs、编辑距离) |
| 图的邻接矩阵 | graph[i][j] == 1 表示 i→j 有边 |
| 棋盘/地图模拟 | 迷宫、岛屿数量、生命游戏 |
| 分组存储 | 每行代表一类数据 |
int rows = matrix.length; int cols = matrix[0].length; // 注意:仅当至少有一行且非空时安全!
安全写法(处理空数组):
if (matrix == null || matrix.length == 0) return; int rows = matrix.length; int cols = matrix[0].length; // 此时可安全访问
int[][] deep = new int[original.length][];
for (int i = 0; i < original.length; i++) {
deep[i] = original[i].clone(); // 克隆每一行
}
| 类型 | 含义 | 特点 |
|---|---|---|
| 浅拷贝(shallow copy) | 复制对象的引用,不复制内部对象 | 新旧数组共享子对象 |
| 深拷贝(deep copy) | 递归复制所有层级的对象 | 完全独立,互不影响 |
| 数组类型 | 浅拷贝是否安全? | 如何实现深拷贝 |
|---|---|---|
| int[], double[] 等基本类型 | ✅ 安全(值拷贝) | arr.clone() 即可 |
| string[] | ⚠️ 表面安全(string 不可变) | 通常 clone() 足够 |
| int[][](二维基本类型) | ❌ 不安全 | 循环 + row.clone() |
| object[](含可变对象) | ❌ 不安全 | 需手动深拷贝每个元素(可能递归) |
注意:string 虽是引用类型,但不可变(immutable),所以浅拷贝通常不会出问题。但如果是 stringbuilder[],就必须深拷贝!
| 误区 | 正确理解 |
|---|---|
| “clone() 就是深拷贝” | ❌ 默认是浅拷贝,除非重写 clone() 方法 |
| “arrays.copyof() 是深拷贝” | ❌ 对多维数组仍是浅拷贝 |
| “基本类型数组不需要深拷贝” | ✅ 正确!因为存的是值,不是引用 |
| 问题 | 说明 |
|---|---|
| 空指针异常 | matrix 为 null,或某行为 null(锯齿数组未初始化) |
| 列数不一致 | 误以为所有行长度相同,直接用 matrix[0].length 遍历所有行 |
| 浅拷贝问题 | 直接赋值导致修改副本影响原数组 |
| 内存浪费 | 用二维数组存稀疏矩阵(此时应考虑 map<pair, value> 或稀疏表示) |
数组(array)和链表(linked list)是两种最基础、最重要的线性数据结构。它们在内存布局、操作效率、适用场景上有本质区别。掌握它们的差异,是算法设计和系统优化的关键。
| 特性 | 数组(array) | 链表(linked list) |
|---|---|---|
| 内存布局 | 连续内存块 | 非连续,靠指针连接 |
| 随机访问 | ✅ o(1)(通过下标) | ❌ o(n)(必须遍历) |
| 插入/删除(中间) | ❌ o(n)(需移动元素) | ✅ o(1)(已知节点时) |
| 插入/删除(头部) | ❌ o(n)(除非用特殊技巧) | ✅ o(1) |
| 插入/删除(尾部) | ✅ o(1)(动态数组均摊) | ✅ o(1)(若有 tail 指针) |
| 空间开销 | 仅数据本身 | 每个节点额外存指针(如 next) |
| 缓存友好性 | ✅ 高(局部性好) | ❌ 低(内存跳跃) |
| 大小固定? | 静态数组固定;动态数组可扩容 | 动态伸缩,无需预分配 |
| 场景 | 推荐结构 | 原因 |
|---|---|---|
| 需要频繁随机访问(如排序、dp) | ✅ 数组 | o(1) 访问 |
| 频繁在头部/中间插入删除 | ✅ 链表 | o(1) 修改指针 |
| 元素数量固定或可预估 | ✅ 数组 | 内存紧凑,性能高 |
| 实现栈(只操作尾部) | ✅ 数组 or 链表 | 两者都 o(1) |
| 实现队列(首尾操作) | ✅ 链表 or 循环数组 | 链表天然支持;数组需循环缓冲 |
| 内存敏感(小对象大量存储) | ✅ 数组 | 链表指针开销大 |
| 需要缓存友好(高性能计算) | ✅ 数组 | 连续内存,预取高效 |
// 三维数组:3 层,每层 4 行,每行 5 列
int[][][] cube = new int[3][4][5];
// 访问
cube[0][1][2] = 10;
// 遍历
for (int[][] layer : cube) {
for (int[] row : layer) {
for (int val : row) {
// ...
}
}
}
实际开发中,三维以上数组较少见,通常用对象封装更清晰。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论