LinkedList源码分析(基于Jdk 11.0.2)
LinkedList和ArrayList都是实现了List接口,这两个类也经常拿来作比较。
总体来讲,它们有以下不同:
- 1,ArrayList底层是数组,LinkedList底层是双向链表
- 2,ArrayList由于底层是数组,所以查找和修改元素快,因为它可以直接操作角标,但是增加、删除操作就慢些,因为要移动大量元素;LinkedList则恰恰相反,因为是用指针指向下一个元素,所以增删只要修改指针的指向就可以达到,而且只需要修改一个,但是LinkedList查找和修改就效率低一些,因为要定位到元素,对于LinkedList来说会慢一些(不想数组一样有下标可以直接操作)
- 3,LinkedList和ArrayList都不是线程安全的
1 2 3 4 5 6 7 8
| public LinkedList() {
}
public LinkedList(Collection<? extends E> c) { this(); addAll(c); }
|
构造函数没做什么。
Node节点:
1 2 3 4 5 6 7 8 9 10 11 12
| 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; } }
|
可以看出它是双向链表。
1,增加元素
1 2 3 4
| //addAll ,在尾部批量增加 public boolean addAll(Collection<? extends E> c) { return addAll(size, c);//以size为插入下标,插入集合c中所有元素 }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| //以index为插入下标,插入集合c中所有元素 public boolean addAll(int index, Collection<? extends E> c) { checkPositionIndex(index);//检查越界 [0,size] 闭区间 //检查越界 [0,size] 闭区间 Object[] a = c.toArray(); //新增元素的数量 int numNew = a.length; //如果新增元素数量为0,则不增加,并返回false if (numNew == 0) return false; //index节点的前置节点,后置节点 Node<E> pred, succ; //在链表尾部追加数据 if (index == size) { //size节点(队尾)的后置节点一定是null succ = null; //前置节点是队尾 pred = last; } else { //取出index节点,作为后置节点 succ = node(index); //前置节点是,index节点的前一个节点 pred = succ.prev; } //链表批量增加,是靠for循环遍历原数组,依次执行插入节点操作。对比ArrayList是通过 //System.arraycopy完成批量增加的 for (Object o : a) {//遍历要添加的节点 @SuppressWarnings("unchecked") E e = (E) o; Node<E> newNode = new Node<>(pred, e, null);//以前置节点 和 元素值e,构建new一个新节点, if (pred == null)//如果前置节点是空,说明是头结点 first = newNode; else//否则 前置节点的后置节点设置问新节点 pred.next = newNode; pred = newNode;//当前的节点为前置节点了,为下次添加节点做准备 }
if (succ == null) {//循环结束后,判断,如果后置节点是null。 说明此时是在队尾append的 last = pred;//则设置尾节点 } else { pred.next = succ;// 否则是在队中插入的节点 ,更新前置节点 后置节点 succ.prev = pred;//更新后置节点的前置节点 } // 修改数量size size += numNew; //修改modCount modCount++; return true; }
|
1 2 3 4 5
| //检查index是否越界(0,size) private void checkPositionIndex(int index) { if (!isPositionIndex(index)) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
|
1 2 3 4
| //如果没有越界返回true private boolean isPositionIndex(int index) { return index >= 0 && index <= size; }
|
插入单个节点
1 2 3 4 5
| //在尾部插入一个节点: add public boolean add(E e) { linkLast(e); return true; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| //生成新节点 并插入到 链表尾部, 更新 last/first 节点 void linkLast(E e) { //记录原尾部节点 final Node<E> l = last; //以原尾部节点为新节点的前置节点 final Node<E> newNode = new Node<>(l, e, null); //更新尾部节点 last = newNode; //若原链表为空链表,需要额外更新头结点 if (l == null) first = newNode; else //否则更新原尾节点的后置节点为现在的尾节点(新节点) l.next = newNode; //链表size加1 size++; ////修改modCount modCount++; }
|
删除节点
1 2 3 4 5 6
| public E remove(int index) { //先检查index是否越界 checkElementIndex(index); //删除元素 return unlink(node(index)); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| E unlink(Node<E> x) { // assert x != null; //当前节点的元素值 final E element = x.item; //当前节点的后置节点 final Node<E> next = x.next; //当前节点的前置节点 final Node<E> prev = x.prev; //如果前置节点为空(说明当前节点原本是头结点) if (prev == null) { //则头结点等于后置节点 first = next; } else { //这里跳过了x节点 prev.next = next; x.prev = null;//将当前节点的 前置节点置空 } //如果后置节点为空(说明当前节点原本是尾节点) if (next == null) { last = prev; } else { next.prev = prev; //将当前节点的 后置节点置空 x.next = null; } //将当前元素值置空 x.item = null; //数量减1 size--; //修改modCount modCount++; //返回被删除的元素值 return element; }
|
##