首先来看一张图
LinkedList实现了List接口,所以具有List集合类的一些特征
- LinkedList的全面说明
(1) LinkedList底层实现了双向链表和双端队列特点
(2) 可以添加任意元素(元素可以重复),包括null
(3) 线程不安全,没有实现同步
2. LinkedList的底层操作机制
(1) LinkedList底层维护了一个双向链表
(2) LinkedList中维护了两个属性first和last分别指向 首节点和尾节点
transient Node<E> first;
transient Node<E> last;
(3) 每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表
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;
}
}
(4) 所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高
3. 源码分析
1. LinkedList list = new LinkedList();
无参构造器
public LinkedList() {
}
2. 这时 linkeList 的属性 first = null last = null
<1>执行add方法
public boolean add(E e) {
linkLast(e); //将新加入的节点往后连
return true;
}
将新的节点,加入到双向链表的最后
void linkLast(E e) {
final Node<E> l = last; //第一次添加时,last == null
final Node<E> newNode = new Node<>(l, e, null); //此时l == null
last = newNode; // last指向该节点
if (l == null) //第一次l为空
first = newNode; //first 也指向该节点
else
l.next = newNode;
size++;
modCount++;
}
当执行第二次add方法时(进入linkLast方法后):令l = last (此时last指向第一个节点,所以l此时也指向第一个节点);紧接着把l赋到了新节点(第二个)的prev位置,所以此时新节点指向第一个节点;再令last指向新节点 (保证last指向的永远都是最后一个节点);此时l不为null (l指向第一个节点),执行l.next = newNode; 也就是令第一个节点的next属性指向新节点,此时构成双向链表。
<2>执行remove方法
linkedList.remove();
public E remove() {
return removeFirst();
}
这里默认删除的是第一个结点
public E removeFirst() {
final Node<E> f = first; //令f指向双向链表的第一个节点
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
执行 unlinkFirst方法, 将 f 指向的双向链表的第一个结点拿掉
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
final E element = f.item; //取出第一个节点的内容
final Node<E> next = f.next; //设置next属性指向第二个节点
f.item = null; //将第一个节点的内容置为null
f.next = null; // help GC 断掉第一个节点的next属性与下一个节点之间的线
first = next; //因为next指向的是第二个节点,此时first指向的也是第二个节点
if (next == null)
last = null;
else
next.prev = null;
//如果第二个节点不为null,则断掉第二个节点的prev属性与第一个节点之间的线(此时第一个节点不指向任何,也没有任何对象指向第一个节点,GC认为其是垃圾将其删除)
size--;
modCount++;
return element;
}
4. ArrayList和LinkedList的比较