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异常。

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/PaperJack/article/details/127778828