我以为链表在添加元素时应该比数组表更快? 我只是测试了添加,排序和搜索元素(数组列表,链表和哈希集)需要多长时间。 我只是对数组列表和链接列表使用java.util类...使用每个类都可以使用的两个add(object)方法。
arraylist在填充列表中执行链表...并线性搜索列表。
这是正确的吗? 我在执行中做错了吗?
***************编辑*****************
我只想确保我正确使用了这些东西。 这是我在做什么:
public class LinkedListTest {
private List Names;
public LinkedListTest(){
Names = new LinkedList();
}
然后,我只使用链表方法,即" Names.add(strings)"。 当我测试数组列表时,几乎是相同的:
public class ArrayListTest {
private List Names;
public ArrayListTest(){
Names = new ArrayList();
}
我做对了吗?
这取决于...您的测试代码是什么样的? 如果将ArrayList初始化为已知大小,则ArrayList可能会更快。 LinkedList带有一些额外的开销,因为每个元素必须包装在一个节点中,以便它可以包含对prev和next的引用。 达到现有容量并将所有元素复制到新阵列时,初始化一个全新的后备阵列会导致ArrayList性能问题。
@Mark-不,不将arraylist初始化为已知大小。
是的,这是正确的。 LinkedList将必须在每次插入时进行内存分配,而ArrayList则可以进行较少的分配,从而将其摊销为O(1)插入。内存分配看起来很便宜,但实际上可能非常昂贵。
由于参考位置的限制,LinkedList中的线性搜索时间可能会更慢:ArrayList元素靠得更近,因此较少的缓存未命中。
当您计划仅在List的末尾插入时,ArrayList是您选择的实现。
好的,我想这很有道理。我做了一些谷歌搜索...一些网站说,添加到链表的末尾或开始应该比对arraylist进行速度更快。为什么?我还阅读了一个站点,该站点表示链表和数组列表的add()方法的时间复杂度均为O(1)。什么?真?!?!
@user:ArrayList的追加(末尾插入)时间摊销为O(1),这意味着在执行较长的追加序列时,其平均O(1)。它们中的任何一个都可以是O(n),但容量要增加一倍,因此下一个n可以使用预分配的空间。
@larsmans,缓存缺失是阵列备份结构的主要卖点。链接结构可能是每个节点上的高速缓存未命中,这使得O(n)迭代实际上显着变慢。
@bestsss:我的意思是LinkedList会导致许多缓存未命中。为了清晰起见,编辑了我的答案。
即使链表根本不包含任何高速缓存未命中,遍历也会变慢。这是因为访问每个项目会导致两次取消引用操作的开销,而不是数组列表所需的单个取消引用操作的开销。
事实证明,使用一系列对象引用创建数组,并且对象本身驻留在内存的无关部分中。如果您有对象数组而不是基元数组,则它与链接列表一样容易受到缓存丢失的影响。
@larsmans,我的评论丝毫没有受到批评,我很高兴有人(除我以外)提到使用Java结构时缓存未命中。主要是赞美。
@Zoe,缓存行相对较短-x86上为64字节。因此,具有8个字节的参考大小,它在ArrayList的缓存未命中后会命中7个缓存。使用LinkedList,您可以假设每个元素上的缓存未命中(当然这不是必须的,但这只是一个猜测)。与任何其他操作相比,高速缓存未命中太慢了(可能慢100倍)。
请记住:
对于给定数量的元素,"原始"性能以及不同结构的缩放比例有所不同;
不同的结构在不同的操作下表现不同,这实际上是选择使用哪种结构时需要考虑的部分。
因此,例如,链表在添加末尾时还有更多工作要做,因为它有一个附加的对象来分配和初始化添加的每个项目,但是无论每个项目的"内在"成本如何,两个结构都将具有O(1)添加到列表末尾的性能,即无论列表大小如何,每次添加都具有有效的"恒定"时间,但是ArrayList与LinkedList之间的常数将有所不同,后者可能更大。
另一方面,链表具有恒定的时间来添加到列表的开头,而在ArrayList的情况下,必须对元素进行"装卸",该操作所花费的时间与元素的数量成正比。但是,对于给定的列表大小(例如100个元素),"笨拙" 100个元素可能比分配和初始化链表的单个占位符对象要快(但是到您说的时候,一千个或一百万个对象或任何阈值,它将不会)。
因此,在测试中,您可能既要考虑给定大小的操作的"原始"时间,又要考虑这些操作如何随着列表大小的增长而扩展。
在某些情况下,链接列表可能比数组列表慢。如果要插入列表的末尾,则很可能阵列列表已分配了该空间。底层数组通常会大量增加,因为这是一个非常耗时的过程。因此,在大多数情况下,只需在后面添加元素即可,而在链表中则需要创建一个节点。对于这两种类型的列表,在前面和中间添加都应具有不同的性能。
在基于数组的列表中,列表的线性遍历总是更快,因为它只能正常地遍历数组。这要求每个单元进行一次解引用操作。在链接列表中,还必须取消引用列表的节点,这要花费两倍的时间。
您为什么认为LinkedList会更快?在一般情况下,插入到数组列表中只是在更新单个数组单元的指针(使用O(1)随机访问)的情况。 LinkedList插入也是随机访问,但必须分配一个"单元"对象来保存该条目,并更新一对指针,并最终设置对要插入的对象的引用。
当然,可能需要定期调整ArrayList的后备数组的大小(如果选择具有足够大的初始容量的情况就不会如此),但是由于该数组呈指数增长,因此摊销成本会很低,并且受其限制O(lg n)复杂度。
简而言之-插入数组列表要简单得多,因此总体上要快得多。
好吧,如果我们定义"插入"是在集合中的任意位置添加元素(这是默认定义),则您必须将所有元素移动到该位置之后。 Id关于有缺陷的基准的第一个提示。
将元素添加到LinkedList的后面时(在Java中,LinkedList实际上是一个双链表),它是O(1)操作,就像将元素添加到它的前面一样。在第i个位置上添加元素大约是一个O(i)操作。
因此,如果您要添加到列表的最前面,则LinkedList会明显更快。
实际上,Arent Java LinkedLists是双重链接的-我认为添加到哪一端并不重要。
啊,我不知道实现细节,但这可能是正确的。我会仔细研究它,并在必要时删除我的答案。
似乎LinkedList确实是一个双重链接列表,很有趣。
@Argote-这样是否会使添加到链表两端的速度比添加到arraylist慢还是快?
好吧,在链表上开始添加应该更快,而在数组列表的末尾添加应该稍微快一点。
@Argote-不。香港专业教育学院现在都尝试。无论哪种方式,链表都比数组表慢。您能看一下我上面的代码片段,并确保我没有做错什么吗?
好吧,正如其他人提到的那样,这也取决于每个列表中的数据量。 LinkedList具有较高的开销,一旦需要在ArrayList上重定位的元素数量超过某个阈值,就可能会抵消这些开销。
ArrayList在访问随机索引数据时速度更快,但是在列表中间插入元素时速度较慢,因为使用链接列表只需要更改参考值即可。但是,在数组列表中,您必须将所有元素复制到插入的索引之后,后面是一个索引。
编辑:没有一个链接表实现记住最后一个元素吗?这样可以加快使用链表最后插入的速度。
实际上,您也不能随机插入LinkedList中,以便找到插入点,这是必需的。
保留对最后一个元素的引用会导致从O(n)到O(1)的追加,但在进行大量追加时仍不会击败动态数组。