java中不存在真正二维数组,只有“数组的数组”;不规则数组是一维引用数组,每个元素指向长度各异的一维数组,内存分两级堆中分配,需手动逐行初始化,遍历时须用arr[i].length获取每行实际长度。

Java里没有真正意义上的“二维数组”,只有“数组的数组”。不规则矩阵数组(也叫锯齿数组)正是基于这个本质实现的——它是一维数组,每个元素又是一个独立的一维数组,而这些子数组长度可以各不相同。
底层结构:一层引用,两层堆内存
声明 int[][] arr = new int[3][] 时,只在堆中创建了一个含3个 null 元素的一维数组(类型是 int[]),它本身存的是3个“地址占位符”。后续执行 arr[0] = new int[2]、arr[1] = new int[5] 等操作,才分别在堆中为每一行单独分配内存块。这三块内存彼此独立、地址不连续,也不受彼此影响。
栈中只保存变量名(如 arr)及其指向第一层数组首地址的引用值;真正的数据全部落在堆中,分两级存放:
- 第一级:存储3个引用,每个引用指向某一行的起始地址
- 第二级:每行各自开辟一段连续内存,存放该行的实际
int值
为什么不能直接用 new int[3][4] 创建不规则结构
new int[3][4] 是语法糖,等价于先建外层数组,再对每行调用 new int[4] ——它强制所有行长度统一。要打破这种约束,就必须跳过自动初始化,手动控制每一行的 new 行为。这也是不规则数组必须分两步声明的原因:先定行数,再逐行分配空间。
如果漏掉某行的赋值(比如没写 arr[2] = new int[3]),访问 arr[2][0] 就会触发 NullPointerException,因为此时 arr[2] 的值仍是 null,还没指向任何堆内存。
遍历时必须用“每行长度”而非固定列数
常规二维数组可用 arr.length 和 arr[0].length 得到行列数,但不规则数组的 arr[0].length 对第三行无效。正确做法是:
- 外层循环用
arr.length控制总行数 - 内层循环每次取
arr[i].length,即当前行真实长度
例如:int[][] j = {{1}, {2,3,4}, {5,6}}; 中,j[0].length==1,j[1].length==3,j[2].length==2 —— 三者互不相等,无法用一个固定列数代替。
典型应用场景印证结构特性
像存储不同班级人数的成绩表、稀疏矩阵的非零行、杨辉三角(第n行有n+1个数)等,天然具有“行长度递增/不等”的特征。用不规则数组,既避免了按最大行宽统一开空间造成的浪费(如全开100列却只用20–35列),又保持了按行随机访问的效率——每行内部仍是连续内存,arr[i][j] 仍能通过偏移量快速定位。
这种“外层灵活、内层紧凑”的设计,正是Java用一维数组模拟多维结构时,在内存利用率和访问性能之间做的务实平衡。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











