使用java arrays.fill(array,int[]subarray)时,为什么子数组共享相同的内存块?

nx7onnlm  于 2021-06-30  发布在  Java
关注(0)|答案(3)|浏览(271)
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堆或常量池中吗?
相关链接:https://stackoverflow.com/a/19199560/13724489

6rvt4ljy

6rvt4ljy1#

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

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

当你跑的时候 Arrays.fill(dp,new int[2]{2,3}); ,则创建一个长度为2的新数组 2 以及 3 ,然后用新数组的引用填充外部数组的所有5个位置。放弃之前的5个内部数组:

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

这当然意味着 dp[1][0] 以及 dp[4][0] 两者都指相同的数组位置,即保持 2 价值观。

ruarlubt

ruarlubt2#

你会这么想吗 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 .

exdqitrt

exdqitrt3#

他们说的是真的:

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]]
在代码中,您只示例化了一个 int[] (一个一维整数数组)。你在做什么 new int[] { 2, 3 }) 只有一次。所以只有一个内部数组。这个 fill 方法将对同一数组的引用填充到外部数组的每个插槽中。事情就是这样。
另外两点
正如holger在评论中所说的,当我们随后构造内部数组时,在声明中构造它们也是一种浪费。省去内部维度,首先只构建外部数组:

int[][] dp = new int[5][]; // No number in the second set of square brackets

顺便说一句,您的这行代码中有一个错误:

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

不允许同时提供数组维度(长度)和内容。在我的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 } 对于外部数组的每个索引,创建五个内部数组。

相关问题