JDK source code - LinkList class

abstract

It mainly analyzes the relevant source code of the java.util.LinkList class.

Definition of LinkList

LinkedList is a linear list (double linked list) implemented by linked list. The elements are orderly and repeatable.

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

LinkList   Field properties

Note that a Node class appears here, which is an internal class in the LinkedList class. Each element in the set represents a Node class object. The LinkedList set is composed of many Node objects, similar to hand-in-hand. Therefore, LinkedList is a two-way linked list.   Note: the first Node prev is null if it does not point to a Node, and the last Node next is null if it does not point to a Node.

//Number of linked list elements (nodes)
transient int size = 0;
//Pointer to the first element
transient Node<E> first;
//Pointer to the last element
transient Node<E> last;

As shown in the figure: each node prev saves the reference of the previous node, and next saves the reference of the next node;

Constructor for Linklist

non-parameter constructor

// Note: there is no need to initialize the size of the linked list. Unlike ArrayList, you need to initialize the size of the array to add elements
public LinkedList() {
}

Generic parameters have parameter constructors

// Add an instance of the Collection of existing elements to the LinkedList
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

Linklist   Add element

We found that every time LinkedList adds an element, it only changes the previous pointer reference and the next pointer reference of the element, and there is no expansion. Compared with ArrayList, capacity expansion is required, and when inserting elements in the middle, all subsequent elements must be moved by one bit. The efficiency of inserting elements is very different between the two.

public boolean add(E e) {
    //Add an element to the end of the linked list
    linkLast(e);
    return true;
}

void linkLast(E e) {
    final Node<E> l = last;//First, use variables to store the tail nodes of the linked list
    final Node<E> newNode = new Node<>(l, e, null);//To add a new element, you need to add a new node. The element content is e,prev is the address reference of the current last node, and next is null
    last = newNode;//Point the tail of the linked list to the new node
    if (l == null)
        first = newNode;//If the linked list is empty and the new node is also set as the head node, the new node is both the head node and the tail node, and the prev and next of the head node are null
    else
        l.next = newNode;//The linked list is not empty. Point the next of the previously stored tail node of the original linked list to the new node
    size++;//Number of nodes plus 1
    modCount++;//As in ArrayList, prevent adding elements during collection traversal
}


public void addFirst(E e) {
    linkFirst(e);
}
private void linkFirst(E e) {
    final Node<E> f = first;//First, use variable f to store the head node of the linked list
    final Node<E> newNode = new Node<>(null, e, f);//Construct the specified element into a new node. The element content is e,prev is null, and next is the address reference of the current first node,
    first = newNode;//Point the head of the linked list to the new node
    if (f == null)
        last = newNode;//If the linked list is empty and the new node is also set as the tail node, the new node is both the head node and the tail node
    else
        f.prev = newNode;//If the linked list is not empty, the previous node of the head node f of the original linked list points to the new node
    size++;//Number of nodes plus 1
    modCount++;//As in ArrayList, prevent adding elements during collection traversal
}


public void addLast(E e) {
    linkLast(e);
}

public void add(int index, E element) {
    //Judge whether the index is out of bounds return index > = 0 & & index < = size;
    checkPositionIndex(index);
    if (index == size)//If the index value is equal to the size of the linked list
        linkLast(element);//Insert the node directly into the tail of the linked list; It has been analyzed above
    else
        linkBefore(element, node(index));
}

//The node is obtained according to the index, because it is a linked list, unlike an array. It is not stored in a continuous location in memory. The value cannot be obtained directly according to the index. You need to look down from the head or tail one by one
Node<E> node(int index) {
    //Size > > 1 means shift, which is divided by 2 to the power of 1
    if (index < (size >> 1)) {//If the index is smaller than half of the linked list
        Node<E> x = first;//Set x as the head node, which means traversing from the beginning node
        for (int i = 0; i < index; i++)//Because you only need to find the index, you can stop when you traverse to the index
            x = x.next;//Move backward from the first node until you move to the previous node of index, and you can find the node at index
        return x;
    } else {//If the index is larger than half of the linked list
        Node<E> x = last;//Let x be the tail node, which means traversing from the last grid node
        for (int i = size - 1; i > index; i--)
            x = x.prev;//Move forward from the last node until you move to index, and then a node can find the node at index
        return x;
    }
}

void linkBefore(E e, Node<E> succ) {
    final Node<E> pred = succ.prev;//Gets the previous node of the index node
    final Node<E> newNode = new Node<>(pred, e, succ);//Construct the newly inserted node. The new node pred is set as the previous node of the original linked list index, and the next node is set as the node at the index of the original linked list
    succ.prev = newNode;//The previous references of the node at the original index are set as new nodes
    if (pred == null)//If the previous node reference of the inserted node is empty
        first = newNode;//Set the head node as a new node
    else
        pred.next = newNode;//The next node of the previous node of the original index is set as the new node
    size++;
    modCount++;
}

Linklist lookup element

public E get(int index) {
    //Judge whether the index is out of bounds return index > = 0 & & index < = size;
    checkElementIndex(index);
    //node(index) as mentioned above, the actual element of the node is obtained here
    return node(index).item;
}

public int indexOf(Object o) {
    int index = 0;
    if (o == null) {//The element found is null
        for (Node<E> x = first; x != null; x = x.next) {//Start from the first node and continue to traverse to the next node
            if (x.item == null)
                return index;//If found, the index is returned
            index++;
        }
    } else {//The element found is not null
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item))//Compare using equals
                return index;//If found, the index is returned
            index++;
        }
    }
    return -1;//If the specified element is not found, - 1 is returned
}

Linklist modify element

public E set(int index, E element) {
    checkElementIndex(index);
    Node<E> x = node(index);//Gets the node at the specified index
    E oldVal = x.item;//Gets the actual element of the node at the specified index
    x.item = element;//Replace the node element at the specified location with the element to be modified
    return oldVal;//Returns the element value of the original node at the index
}

Linklist delete element

  Compared with ArrayList, the deletion of LinkedList does not need to "move" a lot of data, so it is more efficient

public E remove(int index) {
    checkElementIndex(index);
    //Get the node at the specified location first
    return unlink(node(index));
}

E unlink(Node<E> x) {
    final E element = x.item;
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;

    if (prev == null) {//If the previous node reference of the deleted node is null, it means that the deleted node is the head node
        first = next;//Set the head node as the next node to delete the node
    } else {
        prev.next = next;//Point the next of the previous node of the deleted node to the next node of the deleted node
        x.prev = null;//Set the previous node reference of the deleted node to null, otherwise the linked list will be disordered
    }

    if (next == null) {//If the next node reference of the deleted node is null, it means that the deleted node is the tail node
        last = prev;//Set the tail node as the previous node to delete the node
    } else {//Not a tail node
        next.prev = prev;//Point the prev of the next node of the deleted node to the previous node of the deleted node
        x.next = null;//Set the next node reference of the deleted node to null
    }

    x.item = null;//Set the actual element of the deleted node to null for garbage collection
    size--;
    modCount++;
    return element;
}

Summarize the advantages and disadvantages of ArrayList and LinkedList in performance:

  • 1. For ArrayList and LinkedList, the cost of adding an element at the end of the list is fixed. For ArrayList, it is mainly to add an item to the internal array, which may occasionally lead to the expansion of the array; For LinkedList, this overhead is uniform, which is to create a new Node object Node.
  • 2. Inserting or deleting an element in the middle of the ArrayList means that the remaining elements in the list will be moved; The cost of inserting or deleting an element in the middle of the LinkedList is fixed, and only the upper and lower reference addresses of the adjacent nodes of the element need to be changed.
  • 3. LinkedList does not support efficient random element access, because it needs to traverse and search from the first element or the last element; ArrayList can directly get the elements of the corresponding position according to the index.
  • 4. The space waste of ArrayList is mainly reflected in reserving a certain capacity space at the end of the list, while the space cost of LinkedList is reflected in that each element needs to consume a considerable space. Each element not only saves the actual content of the current element, but also the references of the previous node and the next node
  • Therefore, when we add, delete and query elements, we use ArrayList for query, and LinkedList for addition and deletion.

Blog reference

Tags: JDK

Posted on Mon, 22 Nov 2021 03:16:37 -0500 by roice