Look at the source code and say features: LinekdList

Let's talk about the features first, then the source code (the reason for this feature), and then the application scenarios.

Let me talk about the features first (by the way, talk about the difference between ArrayList and LinkedList)

  • Realization of doubly linked list, easy to add and delete . There are pointers at the head and tail of the linked list, adding and deleting only need to change the pointer's point. The ArrayList is an array implementation, suitable for traversal and random query elements.
  • The efficiency of traversal is low . The adjacent nodes of the linked list are not in the adjacent storage space (Memory is not continuous). And ArrayList isMemory contiguousYes, because the space of the entire array object is applied for at one time when the capacity is expanded for the first time, traversing adjacent elements does not need to be addressed again, so the traversal speed of ArrayList is fast .
  • Random read is not supported . The efficiency of the query is unstable. The fastest query is the head and tail nodes, and the slowest query is the nodes in the middle part (because it needs to traverse from the head/end nodes and the memory is not continuous). The ArrayList supports random reading.
  • All elements are allowed, including null . This is the same as ArrayList.
  • Thread is not safe , the same as ArrayList, to be thread safe and available Collections.synchronizedList()创建. Multi-threaded concurrent modification will cause an exception to be thrown.
  • Ordered collection , the input order can be the same as the output order, after all, it also implements the List interface.
  • For the same number of elements, the memory usage of LinkedList should be larger than that of ArrayList . As mentioned below, a node Node has two pointers besides the data item , that is, each node needs more space.

Let's talk about the source code

LinedList implements the Deque, Queue, and List interfaces, which can be used to construct stacks, queues, and ordered collections (sequences).

One, member variables

//一开始是个空链表
transient int size = 0;

//头结点
transient Node<E> first;

//尾结点
transient Node<E> last;

Among them, the node of the linked list is a two-way node, and LinkedList is therefore called a doubly linked list.

Two, the node is a static inner class

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

Node nodes have prev pointers, item (data), and next pointers.
The reason why LinkedList is easy to add and delete: With the prev (pointing to the predecessor node) and next (pointing to the next node) of the node, you can easily add or delete the head and tail of the linked list by modifying the pointers of these two pointers. Node.

Regarding the constructor, there is only one default structure, so just skip it.

Three, add: put the element at the end of the linked list

  1. add(E e): Add an element to the end of the linked list
  2. addAll(int index, Collection<? extends E> c): Add a collection to the specified position of the linked list
add (E and)
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++;
}

It should be noted that when there are no elements in the linked list, the head node first and the tail node last are both null , so the judgment if (l == null) first = newNode;means that when the first element is added to the linked list , the head node first and the tail node last are both new. Element newNode.
If the linked list is not empty, let the next pointer of the last node of the original linked list point to the new element. At this time, the new element becomes the last element of the linked list.

addAll(int index, Collection<? extends E> c): Add the contents of a collection at a certain position
public boolean addAll(int index, Collection<? extends E> c) {
    checkPositionIndex(index);
    Object[] a = c.toArray();
    int numNew = a.length;
    if (numNew == 0)
        return false;

    //创建两个节点pred、succ,分别指向要插入位置的前面和后面的节点
    Node<E> pred, succ;
    if (index == size) {
        succ = null;
        pred = last;
    } else {
        succ = node(index);
        pred = succ.prev;
    }

    //遍历要添加内容的数组
    for (Object o : a) {
        E e = (E) o;
        //创建新节点,头指针指向 pred
        Node<E> newNode = new Node<>(pred, e, null);
        //如果 pred 为null ,说明新建的这个是头节点
        if (pred == null)
            first = newNode;
        else
            //pred 指向新建的节点
            pred.next = newNode;
        //pred 后移一位
        pred = newNode;
    }

    //添加完后需要修改尾指针 last
    if (succ == null) {
        //如果 succ 为null,说明要插入的位置就是尾部,现在 pred 已经到最后一个元素了
        last = pred;
    } else {
        pred.next = succ;
        succ.prev = pred;
    }
	//统计元素个数
    size += numNew;
    modCount++;
    return true;
}

The specific process is as follows: the
Insert picture description hereleft side of the figure is the state before insertion, and the right side is the insertion process and the processing when the insertion is completed.

Four, remove()

There are multiple APIs for deleting nodes

  1. E remove(int index) Delete the node at the specified location.
  2. boolean remove(Object o) Delete an element
  3. removeFirst(): Delete the header of the linked list. removeLast(): Delete the end of the linked list.
remove(int index): delete the specified location node
public E remove(int index) {
    
    
	//检查index是否越界
    checkElementIndex(index);
    return unlink(node(index));
}

E unlink(Node<E> x) {
    
    
    
    final E element = x.item;
    //获取待删除节点后面、前面的节点next、prev
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;

    //如果前面没有节点,说明 x 是第一个
    if (prev == null) {
    
    
        first = next;
    } else {
    
    
        //前面有节点,让前面节点跨过 x 直接指向 x 后面的节点
        prev.next = next;
        x.prev = null;
    }

    //如果后面没有节点,说 x 是最后一个节点
    if (next == null) {
    
    
        last = prev;
    } else {
    
    
        //后面有节点,让后面的节点指向 x 前面的
        next.prev = prev;
        x.next = null;
    }

    x.item = null;
    size--;
    modCount++;
    return element;
}

UnLink must first determine whether the node X to be deleted is at the head or tail of the linked list:

  1. If X is at the head if (prev == null) first = next;, let the next node of X become the new head node first.
  2. If X is at the end if (next == null) last = prev;, let the previous node of X become the new end node last.
  3. See the picture below in other locations.
    Insert picture description here
remove(Object o): delete the specified element

The linked list deletes the specified element Object o to traverse + equals () comparison, if it matches, then call the previous one E unlink(Node<E> x).

public boolean remove(Object o) {
    
    
    if (o == null) {
    
    
        //遍历终止条件,不等于 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;
}
removeFirst(): Delete the head of the linked list and return the deleted element
public E removeFirst() {
    final Node<E> f = first;
    //链表为空,抛异常
    if (f == null)
        throw new NoSuchElementException();
    return unlinkFirst(f);
}

private E unlinkFirst(Node<E> f) {
    // 获取数据,作为返回值
    final E element = f.item;
    final Node<E> next = f.next;
    f.item = null;
    f.next = null;   // help GC
    //现在头节点后边的节点变成第一个了
    first = next;
    
    //如果头节点后面的节点为 null,说明原链表只有一个结点,删除之后链表为空,那得设置first =null and last =null
    if (next == null)
        last = null;
    else
        next.prev = null;
    size--;
    modCount++;
    return element;
}

The flag for judging whether the linked list is empty:, last = null 或者 first=nulleven if the linked list has a node, first and last will not be null. If the linked list has multiple elements, deleting the head node will make the head node first = the next node of the original head node .

Five, query method

List 2 commonly used query APIs

  1. Get the element at the specified position:get(int index)
  2. Get the index of the element:indexOf(Object o)
get(int index): Get the element at the specified position
// 1. 获取指定位置的元素get(int index):要遍历

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

Node<E> node(int 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;
        return x;
    }
}

In order to ensure the efficiency of the search, only half of the linked list is searched at most :

  • If index is close to the head node index < (size >> 1), traverse from the head node
  • If the index is close to the end node index > (size >> 1), traverse from the end node.
int indexOf(Object o): Get the index of the first occurrence of the element
public int indexOf(Object o) {
    
    
    int index = 0;
    if (o == null) {
    
    
        for (Node<E> x = first; x != null; x = x.next) {
    
    
            if (x.item == null)
                return index;
            index++;
        }
    } else {
    
    
        for (Node<E> x = first; x != null; x = x.next) {
    
    
            if (o.equals(x.item))
                return index;
            index++;
        }
    }
    return -1;
}

LinkedList also allows all elements, including null. Iterate from the beginning node to find the index of the first element.

Six, stack and queue operation

Implementation of stack operation (last in, first out)

Mainly operate on the head of LinkedList , involvingpeek()、 push(E e)、pop()

//获取第一个(栈顶),但不删除元素
public E peek() {
    final Node<E> f = first;
    return (f == null) ? null : f.item;
}

//入栈,LinkedList在头部添加元素
public void push(E e) {
    addFirst(e);
}

//出栈,LinkedList删除头部元素,具体操作是在上文的removeFirst的unlinkFirst()方法里
public E pop() {
    return removeFirst();
}
Realization of queue (first in first out)

You can choose to enter at the end of the LinkedList and exit at the head . The methods involved areelement()获得队列头、offer(e)插入队列尾、peek()获得队列头、remove()删除队列头

//获得队列头
public E element() {
    return getFirst();
}

//在尾部添加
public boolean offer(E e) {
    return add(e);
}

//移除并返回队列头部
public E poll() {
    final Node<E> f = first;
    return (f == null) ? null : unlinkFirst(f);   
}

//移除队列头部
public E remove() {
    return removeFirst();
}

The implementation of the queue is somewhat different, there are two methods to get the head of the queue:, element() 、peek()the difference is:

  • element()If the queue is empty, an exception will be thrown, which is consistent with the getFirst() method
  • peek() returns null if the queue is empty

Later on the application

  • The blocking queue LinkedBlockingQueue should be used for this, quite suitable for the producer consumer model.
  • Undo and rollback

Guess you like

Origin blog.csdn.net/qq_44384533/article/details/108661349