ArrayList 和 LinkedList 到底有哪些区别?
Posted 执章学长
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ArrayList 和 LinkedList 到底有哪些区别?相关的知识,希望对你有一定的参考价值。
ArrayList 和 LinkedList 到底有哪些区别?
ArrayList 和 LinkedList
ArrayList 和 LinkedList 之前有那些区别?这个是面试中最常见的一个问题之一了。跟着源码分析,看看两者之间的区别。
底层数据结构不同
进入 ArrayList 的源码,可以看到 ArrayList 的成员变量有一个 Object 数组,这个数据就是用来存放插入的数据。
同样进入 LinkedList 的源码,可以看到成员变量维护者两个节点,一个是 first 节点,一个是 last 节点,实际上,LinkedList 底层是一个双向链表。
操作集合的底层实现不同
插入操作
很容易理解,底层的数据结构不同,直接影响了操作集合的底层操作就是不同的,这里可能需要你对数据结构有一定的了解。
首先是插入,根据我们学过的数据结构的知识,我们知道数组中的元素是存储在一个连续的空间中的,而链表则不是。因此根据索引查找的情况,数组只需要根据偏移量就可以立马查找到数据,这里我们结合源码看。
以下是 ArrayList 的 add 方法
首先当然是需要判断插入时 index 索引是否合法,插入的索引只能是[0,size]
,这也很容易能够理解,否则就容易造成数组越界。
接着调用 System 的 arraycopy 方法,这个方法是一个 navite 方法,说明这个方法不是由 Java 实现的,实现的效果是将数组A的复制到数组B,并且指定区间,这里是将要插入的 index 之后的数据(包括index指向的数据),全都往后移动一位。
例如:数组[1,2,3,4],index = 2,操作后就会变为[1,2,3,3,4]。
接着就是将元素插入到 index 的位置,然后 size++。
以下是 LinkedList 的 add方法
首先,是先判断插入的位置是否是尾端,如果是尾端,直接插入即可,因为是双链表,我们自然维护着最后一个节点的引用。
如果是遇到插入到中间的情况,也就是最普遍的情况,则是先要获取到插入的节点的引用,然后再插入。而获取到插入节点的引用也非常有意思,因为我们知道,我们既拥有头指针,也拥有尾指针,因此我们只要判断插入的索引是靠近哪一个,然后再从这个端点开始遍历。这里size>>1
的效果等同于size/2
。
删除操作
对于 ArrayList 的删除,首先是找到目标元素,然后再将后面的元素全部往前移动。
对于 LinkedList 的删除,首先同样是遍历到要删除的节点,然后直接调用unlink 方法将该节点断开即可。
通过 unlink 方法断开掉要删除的节点。
按照索引查找
对于 ArrayList 来说,无非只需要检查一下参数是否合法,然后返回数组的对应元素即可。
而对于 LinkedList 来说,除了判断参数是否合法外,需要先找到该索引的节点(通过遍历),然后才能返回对应这个节点的值。
总结:其实对于 ArrayList 和 LinkedList 的最大差异,就是底层数据结构的不同,导致在执行同个操作的时候时间效率不一样,比如如果需要频繁按照索引查找元素,则适合使用 ArrayList ;如果是需要频繁删除或者添加元素,则适合使用 LinkedList ,因为 ArrayList 需要移动大量元素,当然 LinkedList 在删除元素前还需要一次遍历进行定位,因此这也不止绝对的。而对于存储空间的选择,实际上 ArrayList 的空间开销主要是在数组上,当然由于扩容机制,会导致数组会有一定的预留空间;而对于 LinkedList 的空间开销主要是在前驱节点和后继节点上,因此每个元素的空间消耗要比 ArrayList 更多。
关于线程安全
ArrayList 和 LinkedList 都是不保证线程安全的。在进行各种操作时并没有上锁,迭代器获取下一个元素的时候会检查是否被修改,如果被修改则抛出ConcurrentModificationException
异常。
以上是关于ArrayList 和 LinkedList 到底有哪些区别?的主要内容,如果未能解决你的问题,请参考以下文章