Java collection – LinkedList source analysis

The basic use

Create a new Java class and walk through how ArrayList works in basic usage

package demo;

import java.util.LinkedList;

public class LinkedListDemo {
    public static void main(String[] args) {
        LinkedList<String> list = new LinkedList<>();
        list.add("demo");
        String s = list.get(0);
        list.remove(0); }}Copy the code

There are 4 steps

  1. Declare a LinkedList
  2. Add an element
  3. Get an element
  4. Delete an element
Start analyzing the source code step by step
  • Declare a LinkedList

    Enter the constructor of LinkedList

    public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable.java.io.Serializable{
        
        transient int size = 0;
    
        transient Node<E> first;
    
        transient Node<E> last;
        
        public LinkedList(a) {}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; }}}Copy the code

    As you can see, nothing is actually done to the LinkedList constructor.

    However, we need to pay attention to the inner class Node

    . We can see that LinkedList is actually a bidirectional LinkedList. For the nodes in it, Node stores Pointers to forward and backward nodes respectively, and its general structure is as follows

  • Add an element

    The trace code goes into the add function and finds that the add function looks like this

    public boolean add(E e) {
        linkLast(e);
        return true;
    }
    
    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++;
        modCount++;
    }
    Copy the code

    As you can see, adding an element directly adds a node to the end of the list and returns true

    Enter the linkLast(E E) function, you can see that there are three steps:

    • Records the current last node
    • Object that declares a new node and assigns the current last node to the new nodeprev
    • Will the currentlastPoint to a new node

    Like the add of ArrayList, the current operation count is recorded by modCount

  • Get an element

    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
    Copy the code

    First check whether the index passed in is valid

    Then go to the node(index) function to find the element

    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;
            returnx; }}Copy the code

    As you can see, the elements are found using a split lookup, starting from fisRT from front to back if less than half, or from last to back if greater

  • Delete an element

    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
    
    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 {
            prev.next = next;
            x.prev = null;
        }
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }
        x.item = null;
        size--;
        modCount++;
        return element;
    }
    Copy the code

    As you can see, remove actually goes into the unlink function. This operation can be divided into four steps:

    • Record the prev and next nodes of the current node
    • ifprevIf it is empty, the first node of the current node willfisrtPoint to the next node of the current node; Otherwise the former node willnextPoints to the next node after the current node
    • ifnextIf it is null, the current node is the tail node and willlastPoint to the previous node of the current node; Otherwise the current node’sprevPoints to the previous node of the current node
    • The current node is empty,sizeThe length of the minus one

    Finally return the deleted element and increment operand

    The whole process can be summarized in the following figure