天天看点

LinkedList底层实现一、什么是LinkedList二、LinkedList的创建三、LinkedList集合的各种插入删除操作四、LinkedList的遍历

一、什么是LinkedList

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
           

根据源码我们可以看出:

  • ①LinkedList继承了AbstractSequentialList,即按次序的List,也就是说不像ArrayList一样具有随机访问元素的能力,也就是说LinkedList支持按顺序访问列表中元素的操作。
  • ②实现了List接口,可进行列表相关操作
  • ③实现了Deque接口,可以将LinkedList当作双端队列进行使用
  • ④实现Clonalbe接口,即覆盖了clone()方法,可以进行克隆操作
  • ⑤实现了java.io.Serializable接口,可以进行序列化,即支持网络传输

LinkedList的本质是双向链表,与 ArrayList 相比,LinkedList 的增加和删除对操作效率更高,而查找和修改的操作效率较低。

二、LinkedList的创建

1、成员变量及数据结构

①transient int size = 0; //LinkedList存放元素个数

②first是双向链表的表头,last指向双向链表的最后一个元素

transient Node first;

transient Node last;

③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;
    }
}
           

存放数据元素,prev指向上一个节点,next指向下一个节点,item为元素值

2、first和last为什么要用transist修饰?

在ArrayList中存放元素的数据结构elementData使用transisit修饰是为了节省空间,防止扩容后数组中的空数据也被序列化。

而LinkedList使用transist修饰是因为LinkedList中使用双向链表保存数据,结点中保存前驱和后继的引用。但是序列化之后前序结点和后继结点的地址都变了,所以我们应该将数据传输过去再在另一边链接为新的链表。

LinkedList的序列化与反序列化方法:

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException {
    // Write out any hidden serialization magic
    s.defaultWriteObject();

    // Write out size
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (Node<E> x = first; x != null; x = x.next)
        s.writeObject(x.item);
}

@SuppressWarnings("unchecked")
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    // Read in any hidden serialization magic
    s.defaultReadObject();

    // Read in size
    int size = s.readInt();

    // Read in all elements in the proper order.
    for (int i = 0; i < size; i++)
        linkLast((E)s.readObject());
}
           

3、LinkedList的创建

LinkedList list = new LinkedList(); // 普通创建方法

或者

LinkedList list = new LinkedList(Collection<? extends E> c); // 使用集合创建链表

使用集合创建链表步骤

  • ①调用addAll( c )方法,而addAll( c )底层调用重写方法addAll(size, c)
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

public boolean addAll(Collection<? extends E> c) {
    return addAll(size, c);
}
           
public boolean addAll(int index, Collection<? extends E> c) {
    checkPositionIndex(index);

    Object[] a = c.toArray();
    int numNew = a.length;
    if (numNew == 0)
        return false;

    Node<E> pred, succ;
    if (index == size) {
        succ = null;
        pred = last;
    } else {
        succ = node(index);
        pred = succ.prev;
    }

    for (Object o : a) {
        @SuppressWarnings("unchecked") E e = (E) o;
        Node<E> newNode = new Node<>(pred, e, null);
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        pred = newNode;
    }

    if (succ == null) {
        last = pred;
    } else {
        pred.next = succ;
        succ.prev = pred;
    }

    size += numNew;
    modCount++;
    return true;
}
           
  • ②addAll方法首先通过checkPositionIndex(index);判断index是否越界,如果越界报越界异常。
  • ③如果Collection为空直接return false
  • ④如果Collection不为空,定义两个指针pred,和succ;如果index等于size,那么说明是在链表末尾插入Collection,使得pred指向last
  • ⑤如果index不等于size,那么一定小于size,使用node(index)方法获得index处的node,是pred指向该node的上一个节点
  • ⑥遍历Collection中的元素,将元素一个个插入进LinkedList中。
    LinkedList底层实现一、什么是LinkedList二、LinkedList的创建三、LinkedList集合的各种插入删除操作四、LinkedList的遍历
  • ⑦最后,若succ为空,说明是在链表尾部插入的,更新last,若不为空,则是在链表中间插入的,执行操作,补全链表
    LinkedList底层实现一、什么是LinkedList二、LinkedList的创建三、LinkedList集合的各种插入删除操作四、LinkedList的遍历

三、LinkedList集合的各种插入删除操作

在前面我们说过,LinkedList可以当作队列也能作为栈,所以linkedList中有许多插入删除的方法。

链表尾部插入数据操作:add()、addLast()、offer()、offerLast()

链表头部插入数据操作:addFirst()、offerFirst()、push()

删除并返回链表尾部的数据:removeLast()

删除并返回链表头部的数据:poll()、remove()、removeFirst()、pop()

获取第一个元素的值:element()、getFirst()、peek()、peekFirst()

获取最后一个元素的值:getLast()、peekLast()

在任意位置插入数据:add(int index, E element)

删除任意位置数据:remove(int index)

特别注意,作为如果使用linkedList作为栈,那么以链表头部作为栈进出的位置。

四、LinkedList的遍历

在ArrayList中已经讲过,LinkedList不支持快速随机访问,所以遍历时采用迭代器遍历更快一些

要理解这个,首先要明白linkedList是一个链表,不支持随机访问,所以linkedList要得到一个元素,即get(i)的时间复杂度时o(n),所以使用一下的遍历方式,时间复杂度就会变为o(n^2)了

for (int i=0; i<size; i++) {
    list.get(i);        
}
           

而我们可以看到linkedList实现的iterator迭代器就是一个一个从first开始遍历到index大于size为止,所以获取数据的时间复杂度为o(1)

LinkedList底层实现一、什么是LinkedList二、LinkedList的创建三、LinkedList集合的各种插入删除操作四、LinkedList的遍历

所以使用LinkedList遍历推荐使用iterator迭代器

List<Integer> list = new LinkedList<>();
Iterator iterator = list.iterator();
while(iterator.hasNext()) {
    iterator.next();
}