使用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
方法将对同一数组的引用填充到外部数组的每个槽中。这就是发生的事情。
另外两点
正如 Holger 在评论中所说,当我们在构建内部 array/s 之后,在声明中也构建它们是一种浪费。省略内维先构造外层数组:
int[][] dp = new int[5][]; // No number in the second set of square brackets
顺便说一句,你的这行代码有一个错误:
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 的新数组,值为 2
和 3
,然后填充外部数组的所有 5 个位置引用那个新数组。丢弃之前的5个内部数组:
dp →→→┌───┐ ┌───┬───┐
│ •→│→→→→→↓ │ 0 │ 0 │
├───┤ ↓ └───┴───┘ ┌───┬───┐
│ •→│→→→↓ ↓ │ 0 │ 0 │
├───┤ ┌───┬───┐ ┌───┬───┐ └───┴───┘
│ •→│→→→│ 2 │ 3 │ │ 0 │ 0 │
├───┤ └───┴───┘ └───┴───┘ ┌───┬───┐
│ •→│→→→↑ ↑ │ 0 │ 0 │
├───┤ ↑ ┌───┬───┐ └───┴───┘
│ •→│→→→→→↑ │ 0 │ 0 │
└───┘ └───┴───┘
这当然意味着 dp[1][0]
和 dp[4][0]
都引用相同的数组位置,即保存 2
值的位置。
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
方法将对同一数组的引用填充到外部数组的每个槽中。这就是发生的事情。
另外两点
正如 Holger 在评论中所说,当我们在构建内部 array/s 之后,在声明中也构建它们是一种浪费。省略内维先构造外层数组:
int[][] dp = new int[5][]; // No number in the second set of square brackets
顺便说一句,你的这行代码有一个错误:
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 的新数组,值为 2
和 3
,然后填充外部数组的所有 5 个位置引用那个新数组。丢弃之前的5个内部数组:
dp →→→┌───┐ ┌───┬───┐
│ •→│→→→→→↓ │ 0 │ 0 │
├───┤ ↓ └───┴───┘ ┌───┬───┐
│ •→│→→→↓ ↓ │ 0 │ 0 │
├───┤ ┌───┬───┐ ┌───┬───┐ └───┴───┘
│ •→│→→→│ 2 │ 3 │ │ 0 │ 0 │
├───┤ └───┴───┘ └───┴───┘ ┌───┬───┐
│ •→│→→→↑ ↑ │ 0 │ 0 │
├───┤ ↑ ┌───┬───┐ └───┴───┘
│ •→│→→→→→↑ │ 0 │ 0 │
└───┘ └───┴───┘
这当然意味着 dp[1][0]
和 dp[4][0]
都引用相同的数组位置,即保存 2
值的位置。