上一篇验证了二维数组是"数组的数组"——这篇真实复现一个由此产生的经典真实陷阱:用Arrays.fill(int[][], int[])给二维数组"填充"每一行,真实得到的不是三份独立数据,而是三个指向同一个数组的引用。
上一篇:接口默认方法菱形冲突真实验证
Arrays类没有专门为二维数组设计的fill重载,但fill(Object[], Object)这个通用重载可以合法接受二维数组(它本质是Object[])和一个一维数组作为填充值。
ArraysFillMultidimDemo.java int[][] arr2d = new int[3][3]; int[] sharedRow = {1, 2, 3}; Arrays.fill(arr2d, sharedRow); // 真实合法调用: fill(Object[], Object)
真实Arrays.fill(arr2d, sharedRow)后, arr2d: [[1, 2, 3], [1, 2, 3], [1, 2, 3]] 真实arr2d[0] == sharedRow: true 真实arr2d[1] == arr2d[2]: true
真实结果证明这次调用真实成功了(没有编译错误、没有运行时异常),打印出来的deepToString()看起来也完全正常——三行数据看起来都是[1, 2, 3]。但真实的引用相等性检测揭示了真相:arr2d[0]、arr2d[1]、arr2d[2]真实全部都是同一个sharedRow对象,不是三份独立的数据副本。这正好利用了上一篇验证过的"二维数组是数组的数组"这个结构——Arrays.fill(Object[], Object)对每个外层位置做的只是一次简单的引用赋值,完全不知道也不关心这个"值"本身又是一个数组。
ArraysFillMultidimDemo.java arr2d[0][0] = 99; // 只想改第一行第一个元素
真实修改arr2d[0][0]=99后, arr2d(所有行都变了): [[99, 2, 3], [99, 2, 3], [99, 2, 3]]
ArraysFillMultidimDemo.java int[][] arr2dCorrect = new int[3][3]; for (int[] row : arr2dCorrect) Arrays.fill(row, 5); // 对每行单独fill
正确写法: 循环对每行单独Arrays.fill(row, 5): [[5, 5, 5], [5, 5, 5], [5, 5, 5]] 正确写法下修改arr2dCorrect[0][0]=100后(其他行不受影响): [[100, 5, 5], [5, 5, 5], [5, 5, 5]]
真实结果证明正确做法是用循环对arr2dCorrect的每一行(每个真实独立的int[]对象)分别调用Arrays.fill(row, value)——因为new int[3][3]这种"完整维度声明"的二维数组,本来每一行就已经是真实独立分配的数组对象(不是共享引用),对每行单独fill不会破坏这种独立性。修改arr2dCorrect[0][0]后,真实只有第一行的数据变化,其余两行完全不受影响——这才是大多数场景下真正想要的行为。
用 identityHashCode 给每一行的数组对象编号,串成动画:编号相同就是同一个对象,改一处会牵连所有引用它的地方。