从内存分配角度看ArrayList与LinkedList

我需要存储大量信息,例如java列表中的“名称”。 项目数量可以改变(或者简而言之,我不能预定义大小)。 我认为从内存分配的角度看,LinkedList比ArrayList更好,对于一个ArrayList,一旦达到最大大小,内存分配自动加倍,因此总是有可能分配比内存更多的内存。需要什么。

我从其他post中了解到,存储在LinkedList中的单个元素比ArrayList占用更多空间,因为LinkedList也需要存储节点信息,但我仍然猜测我已定义的场景LinkedList可能是更好的选择。 此外,我不想进入性能方面(获取,删除等),因为已经讨论过很多内容。

LinkedList可能会分配更少的条目,但这些条目在天文数字上比它们对于ArrayList更昂贵 – 足以使得即使最坏情况的ArrayList在内存方面也更便宜。

(仅供参考,我认为你错了; ArrayList完整时增长1.5倍,而不是2倍。)

参见例如http://code.google.com/p/memory-measurer/wiki/ElementCostInDataStructures:LinkedList每个元素消耗24个字节,而ArrayList在每个元素的最佳情况下消耗4个字节,在最坏的情况下每个元素消耗6个字节。 (结果可能因32位与64位JVM和压缩对象指针选项而异,但在这些比较中, LinkedList成本至少为36字节/元素,而ArrayList最多为8,最差为12。)

更新:

我从其他post中了解到,存储在LinkedList中的单个元素比ArrayList占用更多空间,因为LinkedList也需要存储节点信息,但我仍然猜测我已定义的场景LinkedList可能是更好的选择。 此外,我不想进入性能方面(获取,删除等),因为已经讨论过很多内容。

要清楚,即使在最坏的情况下ArrayList比具有相同元素的LinkedList小4倍。 使LinkedList获胜的唯一可能方法是通过使用故意夸大的值调用ensureCapacity来故意修复比较,或者在添加后从ArrayList删除大量值。

简而言之,基本上不可能使LinkedList赢得内存比较,如果你关心空间,那么在ArrayList上调用trimToSize()会立即使ArrayList再次获胜。 认真。 ArrayList获胜。

…但我仍在猜测我已定义的场景LinkedList可能是更好的选择

你的猜测不正确。

一旦超过了arrays列表的初始容量,后备的大小将在1到2个引用之间乘以条目数。 这是由于用于增长后备arrays的策略。

对于链表,每个节点至少占条目数的3倍,因为每个节点都有一个nextprev引用以及条目引用。 (实际上,它超过3次,因为节点的对象头和填充使用了空间。根据JVM和指针大小,它可以多达6次。)

链接列表使用的空间少于数组列表的唯一情况是,如果您严重高估了数组列表的初始容量。 (对于非常小的清单……)


当你考虑它时,链接列表中唯一真正的优势就是在数组列表中插入和删除元素。 即便如此,这取决于你是如何做到的。

ArrayList对每个对象使用一个引用(或者当它需要的大小加倍时为两个)这通常是4个字节。

LinkedList仅使用其需要的节点,但每个节点可以是24个字节。

所以即使在最坏的情况下,ArrayList也会比LinkedList小3倍。

对于获取ARrayList支持随机访问O(1)但LinkedList是O(n)。 为了从末尾删除,两者都是O(1),从中间的某处删除ArrayList是O(n)

除非你有数百万条目,否则collections的大小不太重要。 首先重要的是条目的大小,无论使用何种集合,都是相同的。

最糟糕的信封背面:

数组中的500,000个名称大小为1,000,000 = 500,000,在分配的数组的未使用部分中有500,000个空指针。

链表中的500,000个条目=每个条目3个指针(节点对象保存当前,上一个和下一个)=内存中的1,5000,000个指针。 (然后你必须添加节点本身的大小)

ArrayList.trimToSize()可能会让您满意。

将此ArrayList实例的容量调整为列表的当前大小。 应用程序可以使用此操作来最小化ArrayList实例的存储。

顺便说一句,在ArrayList Java6中,它不是双倍容量,它是最大尺寸的1.5倍。