学习笔记——LinkedList总结
LinkedList底层是一个双向链表。之所以说是双向的,是因为同时维护了当前节点的前节点和后节点。
可以看下节点的结构:
private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
item就是当前节点的元素,next指向当前节点的后一个节点,prev指向当前节点的前一个节点。
和ArrayList的对比
其实就是链表和数组的对比。
插入,删除:
ArrayList:基于数组实现,通常情况下添加元素很简单,直接将元素添加到预先创建的数组中,没有额外操作。但当数组空间不足时,就涉及到扩容,数组的复制,性能很差。
删除时,需要进行数组元素的复制,性能不高。
LinkedList:基于链表实现,可变长,单纯的插入和删除操作,都比较简单,修改下关联节点的前后"指针"即可。
查询:
ArrayList:支持随机访问,查询效率高。时间复杂度O(1)。
LinkedList:不支持随机访问,按下标索引查询效率较低。可以看下源码:
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; } }
LinkedList中涉及到index的方法,基本都是通过这种方式实现的。
在一个长度为n的LinkedList中,查找一个元素平均要查找(1+n/2)/2次,时间复杂度O(n)。如果是循环遍历的话,那复杂度就是O(n^2)。
所以对不支持随机访问的集合类,尽量不要使用下标索引的方式进行元素的遍历,推荐使用迭代器。(这个在RandomAccess接口说明中有提到)