数组(原始数据或其他数据)无法动态调整大小的原因是什么?
我知道你可以使用ArrayList
,但它背后的实现仍然是一个初始大小的数组(我认为它默认为50),当它超过50时,将创建一个新数组来包含这些元素.
所以,我试图理解一个数组的系统规范,使其不可调整.
这是一个有效的问题,答案与计算机实际工作方式有关.
int[] array = new int[5]
例如,在创建数组时,计算机会在内存中保留五个连续的空格,以便包含在该数组中的数据.但是,之后的内存空间可以立即用于存储其他信息.如果稍后要调整数组的大小,则必须将其他信息移动到其他位置以使数组变大.这是我们不想处理的大量改组,因此计算机架构师不允许使用数组调整大小来简化操作.
数组本质上是一块连续的内存块。根据您将其初始化的内容,它可以相对较小,也可以相对较大。
举例来说,我有一个包含十个元素的数组。
int[] arr = new int[10];
Run Code Online (Sandbox Code Playgroud)
JVM 的底层实现现在必须向操作系统请求 40 个连续字节分配给程序。操作系统强制要求,现在您有 40 个字节,您可以通过熟悉的名称使用它们arr
。
请注意,该数组可能在其两侧共享空间 - 它附近还有其他引用或信息位,并且它不能只是走到其自身的第十一个位置并“声明”它。
假设我们认为 10 太短了。我们需要将其放大十倍。
int arr2 = new int[100];
Run Code Online (Sandbox Code Playgroud)
现在,操作系统必须在内存中找到彼此相邻的 400 字节空间,考虑到对象的生命周期、垃圾收集的运行时间等,这可能很重要,也可能很重要。
调整数组大小并不是简单地将引用移动到几个内存位置 - 而是寻找新的连续内存块来存储数据。
你提到ArrayList
-它很奇怪,因为它由一个“自动”调整大小的数组支持。好吧,调整大小操作有一个问题——成本高昂。
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
Run Code Online (Sandbox Code Playgroud)
这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);
}
Run Code Online (Sandbox Code Playgroud)
本质上,每次需要调整大小时,它都会分配等于原始后备数组 1.5 倍的空间。如果内存相当大,这很快就会变得昂贵ArrayList
- 系统必须出去寻找越来越多的连续内存来分配,这意味着 JVM 必须找到更多的连续空间,这意味着花费更多的时间进行垃圾收集,最终意味着更少的时间表现。
以上甚至没有涉及将数据复制回来。