使用Java Arrays.fill(array,int[] subArray)时,为什么subArray共享同一个内存块?

When using Java Arrays.fill(array,int[] subArray), why subArray share the same memory block?

int[][] dp = new int[5][2];
Arrays.fill(dp,new int[]{2,3});
dp[1][0] = 10;

我以为只有dp[1][0]改成10,结果dp[x][0]都是10(x是0到4)。 我发现了一条与我的问题相关的评论,“此行使每一行都引用相同的内存块,即更改 arr[1][5] 也会更改 arr[100][5]。” 那么为什么这些数组对象共享同一个内存呢?都是在JVM堆还是常量池?

相关link:

您会认为 Array.fill(dp, new int[2]{2,3}) 等同于:

dp[0] = new int[2]{2,3};
dp[1] = new int[2]{2,3};
dp[2] = new int[2]{2,3};
dp[3] = new int[2]{2,3};
dp[4] = new int[2]{2,3};

但是不,更像是:

int[] val = new int[2]{2,3};
dp[0] = val;
dp[1] = val;
dp[2] = val;
dp[3] = val;
dp[4] = val;

您只在行 Array.fill(dp, new int[2]{2,3}) 中创建了 一个 数组。 dp 中的所有子数组都引用您创建的单个数组。 dp[0]dp[1]dp[whatever] 都引用同一个数组。

这是因为当你调用一个方法时,所有的参数都会在方法 运行 之前被求值,所以 new int[2]{2,3}fill 被调用之前被求值。 fill 不会在循环中“运行”表达式 new int[2]{2,3} 并将其分配给数组。 fill都不知道你用的是什么表情!相反,fill 只知道表达式 new int[2]{2,3} 计算出的值——对一个新创建的 int 数组对象的引用。 fill 然后将同一对象分配给 dp.

的每个索引

他们说的是真的:

    int[][] dp = new int[5][2];
    Arrays.fill(dp, new int[] { 2, 3 });
    dp[1][0] = 10;
    
    System.out.println(Arrays.deepToString(dp));

输出:

[[10, 3], [10, 3], [10, 3], [10, 3], [10, 3]]

在代码中,您仅实例化了 one int[](一个一维整数数组)。你只做了一次new int[] { 2, 3 })。所以你只有一个内部数组。 fill 方法将对同一数组的引用填充到外部数组的每个槽中。这就是发生的事情。

另外两点

  1. 正如 Holger 在评论中所说,当我们在构建内部 array/s 之后,在声明中也构建它们是一种浪费。省略内维先构造外层数组:

        int[][] dp = new int[5][]; // No number in the second set of square brackets
    
  2. 顺便说一句,你的这行代码有一个错误:

        Arrays.fill(dp,new int[2]{2,3});
    

    你不能给both一个数组维度(长度)内容。在我的 Eclipse 中,我得到 提供数组初始值设定项时无法定义维度表达式。所以像我在上面所做的那样,在方括号中省略 2

如果你想要五个独立的内部数组,你可以使用setAll方法:

    Arrays.setAll(dp, index -> new int[] { 2, 3 });

现在输出将是:

[[2, 3], [10, 3], [2, 3], [2, 3], [2, 3]]

现在发生的是 setAll 为外部数组的每个索引调用 new int[] { 2, 3 },因此创建了五个内部数组。

当你 运行 int[][] dp = new int[5][2]; 时,你得到一个长度为 5 的外部数组和 5 个长度为 2 的内部数组。所有 5 个内部数组都填充了 0 个值。

dp →→→┌───┐   ┌───┬───┐
      │ •→│→→→│ 0 │ 0 │
      ├───┤   └───┴───┘ ┌───┬───┐
      │ •→│→→→→→→→→→→→→→│ 0 │ 0 │
      ├───┤   ┌───┬───┐ └───┴───┘
      │ •→│→→→│ 0 │ 0 │
      ├───┤   └───┴───┘ ┌───┬───┐
      │ •→│→→→→→→→→→→→→→│ 0 │ 0 │
      ├───┤   ┌───┬───┐ └───┴───┘
      │ •→│→→→│ 0 │ 0 │
      └───┘   └───┴───┘

然后 运行 Arrays.fill(dp,new int[2]{2,3});,您创建一个长度为 2 的新数组,值为 23,然后填充外部数组的所有 5 个位置引用那个新数组。丢弃之前的5个内部数组:

dp →→→┌───┐                  ┌───┬───┐
      │ •→│→→→→→↓            │ 0 │ 0 │
      ├───┤     ↓            └───┴───┘ ┌───┬───┐
      │ •→│→→→↓ ↓                      │ 0 │ 0 │
      ├───┤   ┌───┬───┐      ┌───┬───┐ └───┴───┘
      │ •→│→→→│ 2 │ 3 │      │ 0 │ 0 │
      ├───┤   └───┴───┘      └───┴───┘ ┌───┬───┐
      │ •→│→→→↑ ↑                      │ 0 │ 0 │
      ├───┤     ↑            ┌───┬───┐ └───┴───┘
      │ •→│→→→→→↑            │ 0 │ 0 │
      └───┘                  └───┴───┘

这当然意味着 dp[1][0]dp[4][0] 都引用相同的数组位置,即保存 2 值的位置。