1.初始化

public LinkedList() {
}

并未开辟任何类似于数组一样的存储空间,那么链表是如何存储元素的呢?

2.Node类型

存储到链表中的元素会被封装为一个Node类型的结点。并且链表只需记录第一个结点的位置和最后一个结点的位置。然后每一个结点,前后连接,就可以串起来变成一整个链表。

transient Node<E> first;//指向链表的第一个结点

transient Node<E> last;//指向链表的最后一个结点
   
//LinkedList中有一个内部类Node类型
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; } }

LinkedList的源码分析(基于jdk1.8)-LMLPHP

3.添加元素

public boolean add(E e) {
    //默认链接到链表末尾
    linkLast(e);
    return true;
}
void linkLast(E e) {
    //用l记录当前链表的最后一个结点对象
    final Node<E> l = last;

    //创建一个新结点对象,并且指定当前新结点的前一个结点为l
    final Node<E> newNode = new Node<>(l, e, null);

    //当前新结点就变成了链表的最后一个结点
    last = newNode;

    if (l == null)
      //如果当前链表是空的,那么新结点对象,同时也是链表的第一个结点
        first = newNode;
    else
        //如果当前链表不是空的,那么最后一个结点的next就指向当前新结点
        l.next = newNode;

   //元素个数增加
    size++;

   //修改次数增加
    modCount++;
}

 LinkedList的源码分析(基于jdk1.8)-LMLPHP

4.删除元素

public boolean remove(Object o) {
   //分o是否是null讨论,从头到尾找到要删除的元素o对应的Node结点对象,然后删除
    if (o == null) {
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;
}
E unlink(Node
<E> x) { final E element = x.item; //用next记录被删除结点的后一个结点 final Node<E> next = x.next; //用prev记录被删除结点的前一个结点 final Node<E> prev = x.prev; if (prev == null) { //如果删除的是第一个结点,那么被删除的结点的后一个结点将成为第一个结点 first = next; } else { //否则被删除结点的前一个结点的next应该指向被删除结点的后一个结点 prev.next = next; //断开被删除结点与前一个结点的关系 x.prev = null; } if (next == null) { //如果删除的是最后一个结点,那么被删除结点的前一个结点将变成最后一个结点 last = prev; } else { //否则被删除结点的后一个结点的prev应该指向被删除结点的额前一个结点 next.prev = prev; //断开被删除结点与后一个结点的关系 x.next = null; } //彻底把被删除结点变成垃圾对象 x.item = null; //元素个数减少 size--; //修改次数增加 modCount++; return element; }

LinkedList的源码分析(基于jdk1.8)-LMLPHP

 5.指定位置插入元素

public void add(int index, E element) {
    //检查索引位置的合理性
    checkPositionIndex(index);

    if (index == size)
        //如果位置是在最后,那么链接到链表的最后
        linkLast(element);
    else
        //否则在链表中间插入
        //node(index)表示找到index位置的Node对象
        linkBefore(element, node(index));
}
void linkBefore(E e, Node<E> succ) { // pred记录被插入位置的前一个结点 final Node<E> pred = succ.prev; //构建一个新结点 final Node<E> newNode = new Node<>(pred, e, succ); //把新结点插入到succ的前面 succ.prev = newNode; //如果被插入点是链表的开头,那么新结点变成了链表头 if (pred == null) first = newNode; else //否则pred的next就变成了新结点 pred.next = newNode;
//元素个数增加 size++; //修改次数增加 modCount++; }

LinkedList的源码分析(基于jdk1.8)-LMLPHP

 6.总结

对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高。因为不涉及到移动元素,只需要修改前后结点的关系。也不需要涉及到扩容

此类虽然提供按照索引查找与操作的方法,但是效率不高,如果需要按索引操作,那么建议使用动态数组

12-18 19:38