我对Java有一个基本问题ArrayList
。
当ArrayList
被声明和初始化使用默认构造,对于10个元件的存储器空间被创建。现在,当我添加第11个元素时,会发生什么?是否将创建具有20个(或更多)元素容量的新内存空间(这需要将元素从第一个内存位置复制到新位置)或其他东西?
我在这里检查。但是我没有找到答案。
请分享知识。谢谢。
我对Java有一个基本问题ArrayList
。
当ArrayList
被声明和初始化使用默认构造,对于10个元件的存储器空间被创建。现在,当我添加第11个元素时,会发生什么?是否将创建具有20个(或更多)元素容量的新内存空间(这需要将元素从第一个内存位置复制到新位置)或其他东西?
我在这里检查。但是我没有找到答案。
请分享知识。谢谢。
Answers:
创建一个新数组,并将旧数组的内容复制过来。这就是您在API级别上所知道的。引用文档(我的重点):
每个
ArrayList
实例都有一个容量。容量是用于在列表中存储元素的数组的大小。它总是至少与列表大小一样大。将元素添加到ArrayList后,其容量会自动增长。除了添加元素具有固定的摊销时间成本外,没有指定增长策略的详细信息。
关于特定实现ArrayList
(例如Sun的实现)实际发生的方式,在这种情况下,您可以在源代码中查看细节。但是当然,依靠特定实现的细节通常不是一个好主意...
OutOfMemoryError
。从我几年前的经验来看,我似乎通常会设法抛出错误(为此目的预留了一些内存),但是使用YMMV。
Sun的JDK6:
我相信它会增加到15个元素。不进行编码,而是查看jdk中的grow()代码。
然后int newCapacity = 10 +(10 >> 1)= 15。
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
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);
}
从Javadoc来看,它说这是从Java 2开始的,因此在Sun JDK中可以肯定。
编辑:对于那些没有得到乘数1.5
和int newCapacity = oldCapacity + (oldCapacity >> 1);
>>
是将数字减少一半的右移运算符。因此,
int newCapacity = oldCapacity + (oldCapacity >> 1);
=> int newCapacity = oldCapacity + 0.5*oldCapacity;
=>int newCapacity = 1.5*oldCapacity ;
它将取决于实现,但是来自Sun Java 6源代码:
int newCapacity = (oldCapacity * 3)/2 + 1;
这就是ensureCapacity
方法。其他JDK实现可能会有所不同。
当我们尝试将对象添加到arraylist时,
Java检查以确保现有阵列中有足够的容量来容纳新对象。如果不是,则创建一个更大的新数组,使用Arrays.copyOf将旧数组复制到新数组,并将新数组分配给现有数组。
查看下面的代码(摘自GrepCode.com上的Java ArrayList代码)。
编辑:
public ArrayList()构造一个初始容量为10的空列表。
public ArrayList(int initialCapacity)我们可以指定初始容量。
public ArrayList(Collection c)构造一个列表,该列表包含指定集合的元素,这些元素的顺序由集合的迭代器返回。
现在,当我们使用ArrayList()构造函数时,我们将得到一个Size = 10的ArrayList 。在列表中添加第11个元素时,将在sureCapacity()方法内创建新的Arraylist。
使用以下公式:
int newCapacity= (oldCapacity * 3)/2 +1;
让我们看一下这段代码(jdk1.8)
@Test
public void testArraySize() throws Exception {
List<String> list = new ArrayList<>();
list.add("ds");
list.add("cx");
list.add("cx");
list.add("ww");
list.add("ds");
list.add("cx");
list.add("cx");
list.add("ww");
list.add("ds");
list.add("cx");
list.add("last");
}
1)插入“ last”时将断点放在行上
2)转到添加方法,ArrayList
您将看到
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
3)转到ensureCapacityInternal方法这个方法调用 ensureExplicitCapacity
4)
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
return true;
在我们的示例中,minCapacity等于11,11-10 > 0
因此您需要grow
方法
5)
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)oldCapacity
= 10,因为我们在ArrayList
初始化时未放置此参数,因此它将使用默认容量(10)
2)int newCapacity = oldCapacity + (oldCapacity >> 1);
这里newCapacity等于oldCapacity加上oldCapacity并右移一位(oldCapacity is 10
这是二进制表示形式00001010
,向右移动一位,我们将得到00000101
十进制的5,因此newCapacity
为10 + 5 = 15
)
3)
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
例如你的初始化容量是1,当添加所述第二元件到的ArrayListnewCapacity
将等于1(oldCapacity) + 0 (moved to right by one bit) = 1
在此情况下newCapacity小于minCapacity和elementData
(内部的ArrayList阵列对象)不能保持新的元件,因此newCapacity等于minCapacity
4)
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
检查数组大小是否达到MAX_ARRAY_SIZE(即Integer.MAX-8)。为什么ArrayList的最大数组大小为Integer.MAX_VALUE-8?
5)最后将旧值复制到长度为15的newArray中
上下文Java 8
我在Oracle Java 8实现的上下文中给出答案,因为在阅读所有答案之后,我发现gmgmiller给出了Java 6上下文中的答案,而Java 7给出了另一个答案。但是尚未给出Java 8如何实现大小增加的信息。
在Java 8中,大小增加行为与Java 6相同,请参见grow
ArrayList的方法:
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);
}
关键代码是这一行:
int newCapacity = oldCapacity + (oldCapacity >> 1);
显然,增长因子也为1.5,与Java 6相同。
使用默认构造函数声明和初始化ArrayList时,将创建10个元素的内存空间。
没有。初始化ArrayList后,将为一个空数组分配内存。仅在将第一个元素添加到ArrayList后才为默认容量(10)进行内存分配。
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
* DEFAULT_CAPACITY when the first element is added.
*/
private transient Object[] elementData;
PS没有足够的声誉来评论问题,因此我将其作为答案,因为之前没有人指出这个错误的假设。
在以下情况下,ArrayList确实会增加负载因子的大小:
上下文:JDK 7
在向中添加元素时ArrayList
,public ensureCapacityInternal
内部会发生以下调用和其他私有方法调用,以增加大小。这就是动态增加的大小的原因ArrayList
。在查看代码时,您可以通过命名约定来理解逻辑,因此,我没有添加明确的描述
public boolean add(E paramE) {
ensureCapacityInternal(this.size + 1);
this.elementData[(this.size++)] = paramE;
return true;
}
private void ensureCapacityInternal(int paramInt) {
if (this.elementData == EMPTY_ELEMENTDATA)
paramInt = Math.max(10, paramInt);
ensureExplicitCapacity(paramInt);
}
private void ensureExplicitCapacity(int paramInt) {
this.modCount += 1;
if (paramInt - this.elementData.length <= 0)
return;
grow(paramInt);
}
private void grow(int paramInt) {
int i = this.elementData.length;
int j = i + (i >> 1);
if (j - paramInt < 0)
j = paramInt;
if (j - 2147483639 > 0)
j = hugeCapacity(paramInt);
this.elementData = Arrays.copyOf(this.elementData, j);
}
ArrayList的默认容量为10。一旦容量达到最大容量,ArrayList的大小将为16,一旦容量达到最大容量16,ArrayList的大小将为25,并根据数据大小继续增加。 ...
怎么样?这是答案和公式
New capacity = (Current Capacity * 3/2) + 1
因此,如果默认容量为10,则
Current Capacity = 10
New capacity = (10 * 3/2) + 1
Output is 16
arraylist的默认大小为10。当我们添加第11个.... arraylist时,大小会增加(n * 2)。存储在旧arraylist中的值将复制到大小为20的新arraylist中。