在面试的时候都会被问到集合相关的问题,好比:你能讲讲 ArrayList 和 LinkedList 的区别吗?java
那么我相信你确定可以答上来:ArrayList 是基于数组实现的, LinkedList 是基于链表实现的node
接下来面试官就会连环问了,那你能讲讲,它们都用在什么场景下吗?web
阿粉知道这种程度确定难不倒我们读者的:由于 ArrayList 是基于数组实现的,因此在遍历的时候, ArrayList 的效率是要比 LinkedList 高的, LinkedList 是基于链表实现的,因此在进行新增/删除元素的时候, LinkedList 的效率是要比 ArrayList 高的面试
面试官:哦哦,好的,我大概了解了,我这边没有什么想问的了,您回去等消息能够吗数组
???发生了什么?微信
哈哈,上面模拟了一个面试场景,是想引出来这篇文章的主题:LinkedList 在新增/删除元素时,效率比 ArrayList 高,这是真的吗?app
我相信你也知道套路,通常这么一问,那确定就不是真的了dom
放一张图片,这是通过我测试以后的真实结果ide
由于微信不能放外链的缘故,能够在公众号后台发送 “20200821” 获取测试代码测试
从图中能够看出来, LinkedList 在新增元素时,它的效率不必定比 ArrayList 高,这是要分状况的
若是是从集合头部位置新增元素的话,那确实是 LinkedList 的效率要比 ArrayList 高
可是若是是从集合中间位置或者是尾部位置新增元素, ArrayList 效率反而要比 LinkedList 效率要高
Excuse me ?居然和我之前学的不同?阿粉我学的浅,你别骗我
哈哈哈,为何会这样呢
这是由于 ArrayList 是基于数组实现的嘛,而数组是一块连续的内存空间,因此在添加元素到数组头部时,须要对头部后面的数据进行复制重排,因此效率是蛮低的
可是 LinkedList 是基于链表实现的,在添加元素的时候,首先会经过循环查找到添加元素的位置,若是要添加的位置处于 List 前半段,那就从前向后找;若是位置在后半段,那就从后往前找,因此 LinkedList 添加元素到头部是很是高效的(小声 BB ,这我知道
哦,这你知道?看来基础蛮不错的嘛~
因此当 ArrayList 在添加元素到数组中间时,有一部分数据须要复制重排,效率就不是很高,那为啥 LinkedList 比它还要低呢?这是由于 LinkedList 把元素添加到中间位置的时候,须要在添加以前先遍历查找,这个查找的时间比较耗时
添加元素到尾部操做中, ArrayList 的效率要比 LinkedList 的还要高,这是为啥嘞
由于 ArrayList 在添加的时候不须要什么操做,直接插入就行了,因此效率蛮高的
可是 LinkedList 就不同了,对于 LinkedList 来讲,也不须要查找啥的,直接插入就能够了,可是须要 new 对象,还有变换指针指向对象呀,这些过程耗时加起来可就比 ArrayList 长了
它是有前提的,那就是 ArrayList 初始化容量是足够的状况下,才有上述的特色,若是 ArrayList 涉及到动态扩容,那它的效率确定会下降
删除元素和新增元素的原理是同样的,因此删除元素的操做和新增元素的操做耗时也是很相近
这里就再也不赘述
测试结果很是明显,对于 LinkedList 来讲,若是使用 for 循环的话,效率特别低,可是 ArrayList 使用 for 循环去遍历的话,就比较高
为啥呢?
emmm ,得从源码提及
先来看 ArrayList 的源码吧,这个比较简单
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
可以看到, ArrayList 实现了 List , RandomAccess , Cloneable 还有 Serializable 接口
你是否是对 RandomAccess 这个接口挺陌生的?这是个啥?
可是经过查阅源码可以发现它也只是个空的接口罢了,那 ArrayList 为啥还要去实现它嘞
由于 RandomAccess 接口是一个标志接口,它标识着“只要实现该接口的 list 类,均可以实现快速随机访问”
实现快速随机访问?你能想到什么?这不就是数组的特性嘛!能够直接经过 index 来快速定位 & 读取
那你是否是就能想到, ArrayList 是数组实现的,因此实现了 RandomAccess 接口, LinkedList 是用链表实现的,因此它没有用 RandomAccess 接口实现吧?
beautiful ~就是这样
咱瞅瞅 LinkedList 源码
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
果真,跟我们设想的同样,没有实现 RandomAccess 接口
那为啥 LinkedList 接口使用 for 循环去遍历的时候,慢的不行呢?
我们瞅瞅 LinkedList 在 get 元素时,都干了点儿啥
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
在 get 方法中,主要调用了 node() 方法,由于 LinkedList 是双向链表,因此 if (index < (size >> 1)) 在判断 i 是在前半段仍是后半段,若是是前半段就正序遍历,若是是在后半段那就倒序遍历,那么为何使用 for 循环遍历 LinkedList 时,会这么慢?(好像离真相愈来愈近了
缘由就在两个 for 循环之中,以第一个 for 循环为例
get(0) :直接拿到了node0 地址,而后拿到 node0 的数据
get(1) :先拿到 node0 地址,而后 i < index ,开始拿 node1 的地址,符合条件,而后去拿 node1 的数据
get(2) :先拿到 node0 的地址,而后 i < index ,拿到 node1 的地址, i < index ,继续向下走,拿到 node2 的地址,符合条件,获取 node2 的数据
发现问题了嘛?我就是想要 2 的数据, LinkedList 在遍历时,将 0 和 1 也给遍历了,若是数据量很是大的话,那效率可不就唰唰的下来了嘛
那到如今,我们也就很是明确了,若是是要遍历 ArrayList 的话,最好是用 for 循环去作,若是要遍历 LinkedList 的话,最好是用迭代器去作
我猜你必定会说,阿粉啊,那若是对方就给我传过来了一个 list ,我不知道它是 ArrayList 仍是 LinkedList 呀?我该怎么办呢
还记得 ArrayList 和 LinkedList 有什么不一样吗?是否是 ArrayList 实现了 RandomAccess 接口,可是 LinkedList 没有实现,因此能够从这点去着手解决
暖暖的阿粉在这里给个简单的小 demo ,能够参考下:
public class ListTest {
public static void main(String[] args) {
List<String> arrayList = new ArrayList<String>();
arrayList.add("aaa");
arrayList.add("bbb");
isUseIterator(arrayList);
List<String> linkedList = new LinkedList<String>();
linkedList.add("ccc");
linkedList.add("ddd");
isUseIterator(linkedList);
}
public static void isUseIterator(List list){
if (list instanceof RandomAccess){
System.out.println("实现了 RandomAccess 接口,使用 for 循环遍历");
for (int i = 0 ; i < list.size(); i++ ){
System.out.println(list.get(i));
}
}else{
System.out.println("没有实现 RandomAccess 接口,使用迭代器遍历");
Iterator it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}
}
因此,乖,下次面试官再问你 LinkedList 在新增/删除元素时,效率比 ArrayList 高吗,不要再傻傻的回答是了,拿阿粉这篇文章和他扯皮,保证没问题