先摘选一段
@Test
public void test_LinkedList() {
// 初始化100万数据
List list = new LinkedList(1000000);// 遍历求和int sum = 0;for (int i = 0; i sum += list.get(i);
}
}
- 乍一看可能觉得没什么问题,但是这个遍历求和会非常慢。主要因为链表的数据结构,每一次
list.get(i)
都是从链表的头开始查找,与ArrayList
不同,LinkedList
它时间复杂度是O(n)。那如果说你不知道对方传过来的是LinkedList
还是ArrayList
呢,其实可以通过list instanceof RandomAccess
进行判断。ArrayList
有随机访问的实现,LinkedList
是没有。同时也可以使用增强的for循环或者Iterator
进行遍历。(以上片段来自小傅哥的 握草,你竟然在代码里下毒!,地址:https://mp.weixin.qq.com/s/q9goqjke-hTsx0_QSH_U1w)
链表每个节点我们叫做 Node,Node 有 prev 属性,代表前一个节点的位置,next 属性,代表后一个节点的位置;
first 是双向链表的头节点,它的前一个节点是 null。
last 是双向链表的尾节点,它的后一个节点是 null;当链表中没有数据时,first 和 last 是同一个节点,前后指向都是 null;
因为是个双向链表,只要机器内存足够强大,是没有大小限制的。
因为是链表这种结构,所以添加删除比较容易,只要在头尾节点进行删除就行,
而对于ArrayList这种数组结构,还需要判断进行扩容。
回到上面的问题,为啥get会慢。看源码就知道了。
public E get(int index) { checkElementIndex(index); return node(index).item; } Node node(int index) { // assert isElementIndex(index); if (index < (size >> 1)) { Nodex = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { Nodex = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }
都是截半然后进行for循环查找的。