bigywater.tech
Java 内部原理 · 97

锯齿数组真实验证:Java的二维数组本质是数组的数组,每行可以不同长度

很多从C/C++转过来的开发者以为Java的二维数组是真正的矩形内存块,这篇真实验证它的本质——一个数组,元素恰好也是数组,每一行真实都是独立创建、可以是任意长度的对象。

四行真实各自独立长度
真实验证:new int[4][]手动分配的4行,长度真实分别是1、2、3、4,编译器和运行时完全不要求一致
锯齿存储真实省50%空间
真实计算:n=1000的三角形数据,用锯齿数组只需存储500500个元素,比完整矩形的1000000真实节省了一半

上一篇:Collectors.groupingBy() 真实验证

真实验证:每行真实可以是不同长度的独立数组

手动给二维数组的每一行分配不同长度的内层数组,验证是否真的允许。

JaggedArrayDemo.java int[][] jagged = new int[4][]; // 只分配外层 jagged[0] = new int[]{1}; jagged[1] = new int[]{1, 2}; jagged[2] = new int[]{1, 2, 3}; jagged[3] = new int[]{1, 2, 3, 4};
真实实测
真实验证每行长度不同:
  jagged[0].length 真实是: 1
  jagged[1].length 真实是: 2
  jagged[2].length 真实是: 3
  jagged[3].length 真实是: 4

真实结果证明了 Java 二维数组的本质:int[][] 类型真实就是"元素类型为 int[] 的数组",外层数组的每个元素只是一个指向某个 int[] 对象的引用,这些内层数组彼此完全独立,长度真实可以任意不同——这和 C 语言里 int[4][4] 分配一整块连续矩形内存的模型是完全不同的两种设计。

真实验证:"矩形"写法本质上也是独立的行,可以事后替换

用new int[3][4]这种看起来是矩形的写法创建数组,验证每一行是否真的是独立对象。

JaggedArrayDemo.java int[][] rectangular = new int[3][4]; rectangular[1] = new int[10]; // 事后把某一行替换成不同长度
真实实测
rectangular[0] == rectangular[1] 真实结果: false(每行都是真实独立的数组对象,不是共享同一行)
替换后 rectangular[1].length 真实是: 10(其他行仍是4)
rectangular[0].length 真实保持: 4
"矩形"只是初始化时的巧合,不是语言约束。真实结果证明 new int[3][4] 这种写法只是一个语法糖——它在创建外层数组的同时,真实自动为每一行分配了一个独立的、长度都是 4 的内层数组,但这些内层数组之间没有任何"必须保持同长度"的约束关系。真实把 rectangular[1] 替换成一个长度为 10 的全新数组后,其他行完全不受影响——这条实验直接证明了 Java 里根本不存在"真正的二维数组"这个概念,所有的二维、三维数组都真实是"数组的数组"这一个统一机制的应用,矩形只是一种恰好每行长度相同的特殊情况。

真实应用:用锯齿数组实现杨辉三角,真实节省一半存储

锯齿数组不只是一个语言特性,在真实需要三角形/不规则结构数据的场景下,能真实避免浪费内存。

JaggedArrayDemo.java int[][] triangle = new int[rows][]; for (int i = 0; i < rows; i++) { triangle[i] = new int[i + 1]; // 每行长度真实递增 triangle[i][0] = triangle[i][i] = 1; for (int j = 1; j < i; j++) triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j]; }
真实实测
真实杨辉三角(锯齿数组实现):
  [1]
  [1, 1]
  [1, 2, 1]
  [1, 3, 3, 1]
  [1, 4, 6, 4, 1]

n=1000 时,完整矩形需要存储元素数: 1000000,锯齿三角形只需要: 500500(真实节省约50.0%)

真实结果验证了杨辉三角(帕斯卡三角)这种天然的三角形结构,用锯齿数组存储真实计算正确,而且每一行只分配真正需要的长度,不像用完整矩形数组(int[n][n])那样浪费掉上三角部分永远用不到的空间。对于 n=1000 的场景,真实计算出锯齿存储只需要约一半的元素空间——这是锯齿数组在真实工程场景(邻接表存储图结构、动态规划里的三角形状态表)里的真实价值所在。

分步演示:锯齿数组真实好在哪,又真实容易在哪里栽跟头

每一行都是真实运行这段代码得到的结果。

锯齿数组真实能省下矩形数组浪费的空间,但每一行本身可能是null、长度也可能互不相同,这两点真实是它独有的风险
这一步新验证的场景

    参考与说明

    • 本文全部结果均在本机真实运行 Java 17 得到,锯齿数组行为和存储量计算重复运行完全一致(纯确定性逻辑)。
    • 和数组 clone() 只深拷贝第一层那篇结合看,能更完整理解"数组的数组"这个统一模型在拷贝、比较、内存布局这几个不同角度下的真实一致表现。
    • 没有涉及:三维及以上高维锯齿数组的真实内存布局、锯齿数组在CPU缓存局部性(cache locality)上相比连续矩形数组的真实性能劣势——这两个话题留给系列后面按需再写。
    京ICP备2026067502号-1京公网安备11010502063487号