我在Core Java I书中阅读了下面的代码片段。
将数组列表分配为 新的ArrayList<'员工>(100)//容量为100
与分配新数组不同 新员工[100] //尺寸为100
数组列表的容量与a的大小之间存在重要区别 阵列。如果分配一个包含100个条目的数组,则该阵列有100个插槽,可供使用。 容量为100个元素的数组列表有可能容纳100个元素(并且, 事实上,超过100,以额外的重新分配为代价);但一开始,甚至 在初始构造之后,数组列表根本不包含任何元素。
当我看到源代码数组列表时,构造函数创建了一个给定容量的Object数组,该数组已准备好容纳给定容量的元素(下面是代码片段)。
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
我无法弄清楚作者在上面的文字中提到的实际差异。
答案 0 :(得分:26)
如果使用arr = new Employee[100]
分配新数组,则该数组(arr.length
)的大小将为100.它有100个元素。所有元素最初都为null(因为这是一个对象引用数组),但仍然有100个元素。
如果您执行list = new ArrayList <Employee>(100)
之类的操作,并尝试检查list.size()
,那么您将获得0.列表中没有元素。
在内部,ArrayList
在需要扩展其容量之前分配足够的地方放置100个项目是正确的,但这是一个内部实现细节,列表显示其内容作为&#34;没有存储的项目&#34;。只有当您实际执行list.add(something)
时,才会在列表中包含项目。
因此,虽然列表预先分配存储,但与程序通信的API会告诉您其中没有项目。您无法使用其内部数组中的空项 - 您无法检索或更改它们。
答案 1 :(得分:7)
ArrayList只是表示抽象列表的一种方式,ArrayList的容量是系统如何实现逻辑列表的实现细节。
ArrayList通过使用实际数组&#34;来存储列表的元素。&#34;计算机内存中阵列的实际实现在分配时具有一定的大小;这个大小是ArrayList的容量。除了固定长度数组之外,ArrayList还通过存储列表的逻辑长度来模拟可变大小的列表。因此,如果您有一个容量为10且包含4个逻辑元素的ArrayList,则ArrayList可以表示为长度和数组
(4) | e1 | e2 | e3 | e4 | __ | __ | __ | __ | __ | __ |
其中(4)是列表的逻辑长度,&#39; __&#39;表示被忽略的数据,因为它不是逻辑列表的一部分。如果您尝试访问此ArrayList的第5个元素,它将抛出异常,因为它知道第五个元素尚未初始化。如果我们然后将额外的元素e5附加到列表中,则ArrayList变为
(5) | e1 | e2 | e3 | e4 | e5 | __ | __ | __ | __ | __ |
请注意,容量没有改变,而逻辑长度有,因为底层数组仍然能够处理逻辑列表中的所有数据。
如果您设法向此列表添加十个以上的元素,则ArrayList不会中断。 ArrayList是一个旨在与所有数组操作兼容的抽象。相反,ArrayList在其逻辑长度超过其原始容量时会更改其容量。如果我们要将元素(a1,a2,...,a7)添加到上面的列表中,生成的ArrayList可能看起来像
(12) | e1 | e2 | e3 | e4 | e5 | a1 | a2 | a3 | a4 | a5 | a6 | a7 | __ | __ | __ | __ | __ | __ | __ | __ |
容量为20。
创建ArrayList后,您可以忽略后面所有编程中的容量;逻辑不受影响。但是,在某些操作下系统的性能会受到影响。例如,增加容量可能涉及分配更大的阵列,将第一个阵列复制到第二个阵列然后执行操作。与例如相比,这可能非常慢。链表上的相同操作。因此,选择ArrayList的容量大于或至少可以与实际运行时环境中预期的实际元素数相比是明智的。
答案 2 :(得分:2)
如果您创建一个新数组myArray = new Object[100]
,那么您可以从myArray[0]
读取和写入myArray[99]
(并且您会发现它已满null
)。
如果您创建ArrayList
myList = new ArrayList(100)
然后尝试get
或set
任何元素,您将获得IndexOutOfBoundsException
,因为{{1}在您List
之前,它是空的。
总之,大小为100的数组最初将保留100 add
秒,但null
将为空。
答案 3 :(得分:2)
如果我没有正确理解它,这似乎措辞不当而且可能不正确。
我相信它试图说的是ArrayList的初始容量和ArrayList的初始大小之间存在差异。
List<Employee> employees = new ArrayList<>(100);
int size = employes.size();
size 将为0,而初始容量为100。
您对阅读源代码的方式是正确的。
答案 4 :(得分:1)
区别在于固定大小容器(数据结构)和可变大小容器。
数组是一个固定大小容器,它创建的元素数量是在创建数组时建立的,并且永远不会更改。 (当创建数组时,所有这些元素都将具有一些默认值,例如,对于引用类型为null或对于int为0,但它们都将在数组中存在:您可以为每个元素编制索引。)
list 是可变大小容器,其中的元素数量可以更改,范围从0到您想要的任意数量(受实现限制)。创建后,元素的数量可以增长或缩小。您始终可以通过索引检索任何元素。
但Java概念List
实际上是一个接口,它可以通过许多不同的方式实现。因此,ArrayList
,LinkedList
等。列表后面有一个数据结构来实际保存元素。并且该数据结构本身可能是固定大小或可变大小,并且在任何给定时间可能具有列表中元素数量的确切大小,或者它可能具有一些extra
“缓冲区”空间。
例如,LinkedList
在其基础数据结构中始终具有与其所代表的列表中的“元素位置”完全相同的数量。但ArrayList
使用固定长度数组作为其后备存储。
对于ArrayList
,在任何给定时间,列表中元素的数量可能与其后面的数组可以容纳的元素数量不同。元素的“额外”位置只包含空值或0或其他值,但ArrayList
从不允许您访问这些位置。当您向ArrayList
添加元素时,它们会占用底层数组中的更多位置,直到最后底层数组已满。您添加到ArrayList
的 next 元素会导致一个全新的固定大小数组 - 比“当前”数组稍大 - 要分配,并将所有列表元素复制到它(原始数组被丢弃)。为了防止这种昂贵的操作(分配和复制)经常发生,新数组比当前数组(通过某种因素)大,因此具有当时不会保存列表元素的元素 - 它们是空的(null)或0)。
因此,由于所表示的列表中的元素数量(可能)存在差异,并且实现数据结构可以容纳的元素数量存在两个概念。< / p>
列表的大小是其中的元素数量。列表的容量是支持数据结构此时可以支持的元素数。在列表中添加或删除元素时,大小将发生变化。当您使用的列表的实现需要时,容量将会改变。 (当然,尺寸永远不会大于容量。)
(顺便说一下,对于固定大小的容器, size 经常被称为长度,因此数组的属性 length ,字符串有一个方法< strong> length()。不同的语言 - 有时甚至是相同的语言 - 使用“大小”和“长度”不一致,但它们总是表示大小,术语“容量”总是用于底层数据结构的大小/长度。)
答案 5 :(得分:0)
让我们用一个现实生活中的例子。 考虑乘坐18座公交车,可容纳18名乘客。在任何给定时间乘客的大小可以小于十八但不大于。如果乘客人数为18人,则无法容纳另一名乘客。
在ArrayList中,容量与我们总线的容量有一些共同点,它定义了可以容纳的元素数量。然而,与我们的总线不同,容量会扩展到容纳Integer.MAX_VALUE之前的元素数量。
大小相同,就像我们的总线一样,列表中元素的大小不能超过容量。想象一下,有50名乘客乘坐18座公交车!你肯定不想去那辆公共汽车。
答案 6 :(得分:0)
示例: ArrayList aListNumbers = new ArrayList(20);
将创建一个初始容量为20的ArrayList对象。 这意味着ArrayList在需要调整内部数组的大小之前将能够容纳20个元素。