ArrayList:大小如何增加?
Posted
技术标签:
【中文标题】ArrayList:大小如何增加?【英文标题】:ArrayList: how does the size increase? 【发布时间】:2011-05-25 22:30:11 【问题描述】:我有一个关于 Java ArrayList
的基本问题。
当使用默认构造函数声明和初始化ArrayList
时,会创建10 个元素的内存空间。现在,当我添加第 11 个元素时,会发生什么?是否会创建具有 20 个(或更多)元素容量的新内存空间(这需要将元素从第一个内存位置复制到新位置)或其他什么?
我检查了here。但我没有找到答案。
请分享知识。 谢谢。
【问题讨论】:
最好的方法是实际阅读源代码。但要小心。这里是龙。 Here 是 OpenJDK 6 中 ArrayList 的来源。请注意,它有很多实现(GNU Classpath、Apache Harmony、OpenJDK 等),它们可能会有所不同。 大多数实现增长了 1.5 倍:octoperf.com/blog/2018/03/19/java-arraylist 【参考方案1】:ArrayList 是 Collection 接口的类。 Java 是开源语言,我们可以更改它的实现。 这里java预定义的实现是: 新容量 = (currentCapacity*3)/2 + 1; 要么 在 JAVASE8 中 新容量=旧容量+(旧容量>>1);
【讨论】:
这与this other answer中的相同。【参考方案2】:让我们看看这个测试用例(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
的add方法
你会看到
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
进入ensureCapacityInternal方法,该方法调用ensureExplicitCapacity
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);
让我们描述每个步骤:
oldCapacity
= 10 默认情况下,java 8 arraylist 的容量是 10 ,你可以通过在构造函数中传递另一个值来覆盖它
int newCapacity = oldCapacity + (oldCapacity >> 1);
这里 newCapacity 等于 oldCapacity 加上 oldCapacity 右移一位
(oldCapacity is 10
这是二进制表示 00001010
向右移动一位将使 00000101
是十进制的 5,因此 newCapacity
是 10 + 5 = 15
)
如果 (newCapacity - minCapacity new ArrayList<>(1)),当您添加第二个元素时,newCapacity
将等于 1(oldCapacity) + 0 (moved to right by one bit) = 1
在这种情况下,newCapacity 小于 minCapacity 并且 elementData
( 存储数据的 arrayList 内的对象 arrayObject[]
) 不能容纳新元素,因此 newCapacity 将等于 minCapacity
如果(新容量 - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity);
检查数组大小是否达到 MAX_ARRAY_SIZE(即 Integer.MAX - 8)Why the maximum array size of ArrayList is Integer.MAX_VALUE - 8?
-
最后它将旧值复制到长度为 15 的 newArray
【讨论】:
【参考方案3】:Java 中的 ArrayList 在满后会增长 50% 的原始容量。
【讨论】:
欢迎来到 Stack Overflow。与其他答案相比,此答案增加了哪些新信息?【参考方案4】:Arraylist 默认容量为 10。
[1,2,3,4,5.....10]
如果你想在java中增加Arraylist的大小,你可以应用这个
公式-
int newcapacity,当前容量;
新容量=((当前容量*3/2)+1)
arralist 将为 [1,2,3,4,5.....10,11],arraylist 容量为 16。
【讨论】:
嗨 Shobha,欢迎来到 ***。请不要将您的整个答案插入代码块中。请仅将代码块用于代码并将文本留在外面。此外,您可能希望在粘贴代码之前对其进行测试:您不能在变量名中使用空格,因此current capacity
不是变量,您的代码将无法编译。【参考方案5】:
直到 JDK 6,容量随着公式 newCapacity = (oldCapacity * 3/2) + 1
增长。
在 JDK 7 及更高版本中,公式更改为 newCapacity = oldCapacity + (oldCapacity >> 1)
。
所以如果初始容量为10
,那么新容量将为16 in JDK6
和15 in above JDK7
【讨论】:
【参考方案6】:(oldSize * 3)/2 + 1
如果您使用默认构造函数,则 ArrayList
的初始大小将为 10
否则您可以在创建 ArrayList
的对象时传递数组的初始大小。
示例:万一默认构造函数
List<String> list = new ArrayList<>();
list.size()
例子:万一参数化构造函数
List<String> list = new ArrayList<>(5);
list.size()
【讨论】:
【参考方案7】:从JDK源代码中,我找到了下面的代码
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
【讨论】:
【参考方案8】:ArrayList 在以下情况下确实会增加负载因子的大小:
初始容量: 10 负载系数: 1(即当列表已满时) 增长率: current_size + current_size/2上下文: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);
【讨论】:
【参考方案9】:在 Jdk 1.6 中: 新容量=(当前容量*3/2)+1;
在 Jdk 1.7 中:
int j = i + (i >> 1); 这和 新容量=(当前容量*1/2)+当前容量;
ex:size 会增加 :10-->15-->22-->33
【讨论】:
【参考方案10】:static int getCapacity(ArrayList<?> list) throws Exception
Field dataField = ArrayList.class.getDeclaredField("elementData");
dataField.setAccessible(true);
return ((Object[]) dataField.get(list)).length;
在修改arraylist时使用上述方法检查大小。
【讨论】:
【参考方案11】:ArrayList 的默认容量是 10。 一旦Capacity达到最大容量,ArrayList的Size将为16,一旦达到最大容量16,ArrayList的大小将为25,并根据Data size不断增加.....
怎么样?这是答案和公式
New capacity = (Current Capacity * 3/2) + 1
所以,如果默认容量是10,那么
Current Capacity = 10
New capacity = (10 * 3/2) + 1
Output is 16
【讨论】:
【参考方案12】:ArrayList
的大小总是随着n+n/2+1
而增加。
【讨论】:
你能补充一些解释吗?很短【参考方案13】:当使用默认构造函数声明和初始化 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;
附:没有足够的声誉来评论问题,所以我将其作为答案,因为之前没有人指出这个错误的假设。
【讨论】:
【参考方案14】:上下文 java 8
我在Oracle java 8实现的上下文中给出了我的答案,因为在阅读了所有答案后,我发现gmgmiller给出了java 6上下文中的答案,并且在上下文中给出了另一个答案java 7. 但是java 8是如何实现大小增加的还没有给出。
在java 8中,大小增加行为与java 6相同,见ArrayList的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);
关键代码是这一行:
int newCapacity = oldCapacity + (oldCapacity >> 1);
这么清楚,增长因子也是1.5,和java 6一样。
【讨论】:
对于无法获得“1.5”推导的人。将 oldCapacity 视为 x,则 newCapacity = x + x/(2^1) = x +x/2 = 1.5x 操作符 '>>',它在移位时进行符号扩展,对于某些人来说可能有点混乱,因为它不被视为 div 操作符,因为它不与负数一起使用价值观。在这种情况下,它只是由于长度始终> = 0而起作用。作为 div 操作符 '>>>' 应该主要使用。【参考方案15】:当我们尝试将一个对象添加到数组列表中时,
Java 检查 以确保现有数组中有足够的容量来容纳新对象。如果没有,创建一个更大的新数组,旧数组被复制到新数组,使用 Arrays.copyOf 和新数组分配给现有数组。
查看下面的代码(取自 GrepCode.com 上的 Java ArrayList 代码)。
编辑:
public ArrayList() 构造一个初始容量为 10 的空列表。
public ArrayList(int initialCapacity)我们可以指定初始容量。
public ArrayList(Collection c) 构造一个包含指定集合元素的列表,按集合迭代器返回的顺序排列。
现在,当我们使用 ArrayList() 构造函数时,我们会得到一个 Size=10 的 ArrayList 在列表中添加第 11 个元素时,在 ensureCapacity() 方法中创建新的 Arraylist。
使用以下公式:
int newCapacity= (oldCapacity * 3)/2 +1;
【讨论】:
那个动作 :) 当你在近 2 年后在谷歌搜索中得到答案时【参考方案16】:Sun 的 JDK6:
我相信它会增长到 15 个元素。没有把它编码出来,而是看着 grow() jdk中的代码。
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 ;
【讨论】:
是的,阅读源代码是理解行为的最佳方式。我也阅读了代码并得到了相同的答案。对于 jdk 8 也是如此。【参考方案17】:当使用默认值声明和初始化 ArrayList 构造函数,将创建 10 个元素的内存空间。 现在,当我添加第 11 个元素时,会发生什么
ArrayList 创建一个具有以下大小的新对象
即旧容量*3/2+1 = 10*3/2+1 =16
【讨论】:
【参考方案18】:arraylist 的默认大小是 10。当我们添加第 11 个时 ....arraylist 增加大小(n*2)。将旧arraylist中存储的值复制到大小为20的新arraylist中。
【讨论】:
这不会添加 2010 年原始答案中未提及的任何内容。 当你添加第 11 个元素时,容量将是 15 而不是 20。【参考方案19】:创建一个新数组,并复制旧数组的内容。这就是您在 API 级别所知道的全部内容。引用the docs(我的重点):
每个
ArrayList
实例都有一个容量。容量是用于存储列表中元素的数组的大小。它总是至少与列表大小一样大。随着元素被添加到 ArrayList,它的容量会自动增长。 除了添加一个元素具有恒定的摊销时间成本这一事实之外,没有具体说明增长策略的细节。
就ArrayList
的特定实现(例如Sun 的)实际发生的情况而言,在他们的情况下,您可以在源代码中看到血淋淋的细节。但当然,依赖特定实现的细节通常不是一个好主意...
【讨论】:
将创建一个新数组并复制旧数组的内容。那么旧数组会在内存中还是被删除呢? @VikasKumarSingh:旧的有资格进行垃圾收集,就像任何不再有任何引用它的对象一样。何时(或即使)发生这种情况取决于 JVM。 复制元素的旧 ArrayList 会发生什么情况? GC 会回收该内存吗? @Ravi.Kumar:没有旧的ArrayList,只有旧的数组。是的,ArrayList 释放了它对旧数组的引用,这是对它的唯一引用,这意味着它有资格进行 GC。 @Denson - 每当 JVM 无法分配内存时,它要么直接死掉(或者尝试分配的速度太慢以至于它可能已经死了),要么成功抛出OutOfMemoryError
.在我看来(根据我 年 前的经验)它通常确实会抛出错误(为此目的预留了一些内存),但是 YMMV。【参考方案20】:
这将取决于实现,但来自 Sun Java 6 源代码:
int newCapacity = (oldCapacity * 3)/2 + 1;
在ensureCapacity
方法中。其他 JDK 实现可能会有所不同。
【讨论】:
【参考方案21】:通常,ArrayList 类型容器的内存会增加一倍。因此,如果您最初有 10 个项目的空间并且您添加了 10 个,则第 11 个项目将添加到一个新的(内部)20 个项目的数组中。这样做的原因是,如果数组以固定大小增量递增,则添加项目的增量成本从 O(n^2) 减少到每次内部数组已满时将大小加倍时的 O(n)。
【讨论】:
【参考方案22】:会发生的情况是创建一个包含 n*2 个空格的新数组,然后复制旧数组中的所有项目,并将新项目插入第一个空闲空间。总而言之,这导致 ArrayList 的添加时间为 O(N)。
如果您使用的是 Eclipse,请安装 Jad 和 Jadclipse 以反编译库中保存的 JAR。我这样做是为了阅读原始源代码。
【讨论】:
以上是关于ArrayList:大小如何增加?的主要内容,如果未能解决你的问题,请参考以下文章