无法动态调整数组(原始数据或其他数据)的原因是什么?
我知道你可以使用ArrayList
,但它背后的实现仍然是一个初始大小的数组(我认为它默认为50),当它超过50时,将创建一个新的数组来包含那些元件。
所以,我试图理解一个数组的系统规范,使其不可复制。
答案 0 :(得分:5)
这是一个有效的问题,答案与计算机实际工作方式有关。
例如,当您使用int[] array = new int[5]
创建数组时,计算机会在内存中保留五个连续的空格,以便包含在该数组中的数据。但是,之后的内存空间可以立即用于存储其他信息。如果稍后要调整数组的大小,则必须将其他信息移动到其他位置以使数组变大。这是我们不想处理的大量改组,因此计算机架构师不允许调整数组大小以使事情变得更简单。
答案 1 :(得分:4)
阵列是一个连续的内存块。根据您初始化的内容,它可能相对较小,或相对较大。
比方说,我有一个包含十个元素的数组。
int[] arr = new int[10];
JVM的底层实现现在必须要求操作系统将40个连续字节分配给程序。操作系统有义务,现在您有40个字节,您可以使用熟悉的名称arr
。
请注意,此数组可能在其两侧共享空间 - 附近有其他引用或信息位,它不能只是走到自身的第11个位置并“声明”它。
假设我们认为10太短了。我们需要把它做大 - 增加十倍。
int arr2 = new int[100];
现在操作系统必须在内存中找到彼此相邻的400个字节的空间,考虑到对象的生命周期,垃圾收集的运行时等等,这可能是也可能不是微不足道的。
调整数组大小不仅仅是在几个内存位置上移动引用 - 它是关于查找连续内存的新块来存储数据。
你提到ArrayList
- 它的好奇之处在于它由一个“自动”调整大小的数组支持。嗯,有一个关于调整大小操作的问题 - 它很昂贵。
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
ensureCapacityInternal
做了一些有趣的事情......最终调用ensureExplicitCapacity
...最终调用grow
:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
基本上,每次需要调整大小时,它会分配相当于原始后备阵列1.5倍的空间。如果ArrayList
非常大,这会非常快地获得昂贵 - 系统必须走出去并找到越来越多的连续内存来分配,这意味着JVM必须找到更多连续的空间,意味着更多的时间花在垃圾收集上,最终意味着更少的性能。
以上内容甚至没有覆盖重新复制数据。
答案 2 :(得分:3)
假设您定义了一个16字节的数组,一个整数&amp;另一个整数。
现在你希望调整它的大小......
======================================================
|| || || || || || || || || || || || || || || || || || ---> (Memory)
======================================================
\________________/\____/\____/
---------------- ---- ----
Array(16) Int Int
上面的数组是否容易调整大小?
新的数组必须分配给下一个可用的空闲内存块,因为程序已经为整数保留了块。
答案 3 :(得分:2)
要解决此问题,矢量就在那里。
你应该使用向量就像动态分配内存一样。