Chapter 7 Analysis of LinkedList Source Code of Java Collection

Preface

As a data processing tool, a computer needs to do a lot of data processing internally. The relationship between data and data is called data structure. In the process of computer development, many data structures were born, and linear tables are one of the most basic data structures. It represents an ordered collection of a certain type of data with the same properties

	L = (a1, a2, …, ai,ai+1 ,…, an)

The linear structure is divided into physical continuity and logical continuity in physical memory.
Physical continuity means that the logical order of data is determined according to the distribution of memory addresses, which is called sequential storage structure.
Logical continuity means that the logical sequence of data is determined based on the mutual links of memory addresses, which is called a chain storage structure.

The source code analysis of ArrayList in the previous chapter is realized by data, which is the representative of the realization of "Sequential Storage Structure Collection of Linear List" in Java language.
Website: Chapter 6: Analysis of the ArrayList Source Code of JAVA Collections
And this chapter LinkedList is the representative of the realization of "Linear List Chained Storage Structure Collection". These two chapters are the realization of the linear table data structure.

Chain structure of linear watch

The linear table of the chain structure is directional. For example, the chain structure just above, strictly speaking, it belongs to the one-way chain structure.
In a singly linked list, it is divided into whether it is connected end to end. If it is, it is called a one-way circular chain structure.

Of course, in addition to one-way, there are two-way. It is called a two-way chain structure. There are also doubly linked lists in doubly linked lists.

How to construct a linked list

From the above figure, you can see that the linked list has data blocks, and a piece of data is called a node (the following knots, nodes. Synonymous). For example: node 1, node 2...
a node can contain a lot of data, in addition to node data, there is also a destination address pointing to the previous node, or the next destination address.
So a node contains data and address.
If there is only one address, it is called a one-way node.
If there are two addresses before and after, it is called a bidirectional node.
Multiple nodes constructed together are called a linked list.
The java language is also a linked list constructed based on this theory. The following leads to the protagonist of this chapter, LinkedList.

LinkedList(JDK1.8)

LinkedList is a collection container with a chain structure constructed by Java. Each element in the collection is a node, and the elements are linked to each other through the front and back addresses. It should be noted that LinkedList is constructed based on the common doubly linked list in the linked list structure.
The core of the two-way chain structure is that each node has a pre-drive address and a post-drive address. Therefore, LinkedList maintains an internal class Node with such functions.

// 结点构造
 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;
    }
}

In addition, LinkedList also maintains two variables pointing to the current end node;

transient Node<E> first;  // 第一个
transient Node<E> last;  // 最后一个

The above is the core of LinkedList. Let's expand on the core operations based on these cores.

	【增】
	private void linkFirst(E e)
	void linkLast(E e)
	void linkBefore(E e, Node<E> succ)
	
	【删】
	private E unlinkFirst(Node<E> f)
	private E unlinkLast(Node<E> l)
	E unlink(Node<E> x)
	
	【查】
	Node<E> node(int index)

The content of the entire LinkedList is basically like this, and there is an iterator behind.
LinkedList is simpler than ArrayList. The bottom layer of ArrayList is an array. There are many operations on the array, many changes, and operations such as expansion.
There is only one node class inside LinkedList, as well as operations on the node class. Two pointers at the beginning and the end are maintained internally to mark the position of the first and last one in the current linked list.
Each node in the linked list is unique, and the node will not be repeated. Even if the elements stored in the node are the same.
Let's start the source code analysis of the core method.

Structure chart

Insert picture description here

Source code analysis

One, increase

	private void linkFirst(E e)			// 添加结点作为开头
    void linkLast(E e)   				// 添加结点到末尾
	void linkBefore(E e, Node<E> succ)  // 在指定结点前,添加结点。

In general, this module is the three basic operations on the linked list: increase at the head, increase at the tail, and increase at the specified position.
(1) The three operations all involve the modification of the linked list structure, so the variable modCount is involved;
(2) The three operations all involve the change of length, so the variable size is involved;
(3) Adding to the head involves changing the position of the head pointer, Adding to the tail involves changing the position of the tail pointer. Adding in the middle does not involve the first pointer position.

linkFirst (E e)

private void linkFirst(E e) {
    final Node<E> f = first;						 // 原来第一个节点
    final Node<E> newNode = new Node<>(null, e, f);  // 用需要增加的元素构造新节点,其中头指针为null,尾指针为原来的第一个节点。
    first = newNode;				// 链表头指针重新定位到新节点。
    if (f == null)					// 如果原来的头结点不存在,证明原来链表中没有元素。尾指针也指向新节点。
        last = newNode;
    else
        f.prev = newNode;		    // 之前说了这是一个双向链表,前面构造元素只是让新节点指向原来的第一个节点,这个操作是原来的第一个节点指向新节点。双向的。
    size++;
    modCount++;
}

LinkLast (E e)
is basically the same as the above method, one is the first and the other is the last.

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

linkBefore(E e, Node succ)
adds a node before the specified node. succ is a designated node, this node cannot be null

void linkBefore(E e, Node<E> succ) {
    // assert succ != null;
    final Node<E> pred = succ.prev;	
    final Node<E> newNode = new Node<>(pred, e, succ);
    succ.prev = newNode;
    if (pred == null)
        first = newNode;
    else
        pred.next = newNode;
    size++;
    modCount++;
}

Two, delete

	private E unlinkFirst(Node<E> f)
	private E unlinkLast(Node<E> l)
	E unlink(Node<E> x)

Corresponding to the addition of modules, the deletion of the linked list is also the three operations: delete the first node, delete the second node, and delete the specified node.
Each node in the linked list is unique.
unlinkFirst(Node f)

	private E unlinkFirst(Node<E> f) {
    // assert f == first && f != null;
    final E element = f.item;	
    final Node<E> next = f.next;	
    f.item = null;		
    f.next = null; // help GC   
    first = next;
    if (next == null)
        last = null;
    else
        next.prev = null;
    size--;
    modCount++;
    return element;
}

unlinkLast(Node l)

private E unlinkLast(Node<E> l) {
    // assert l == last && l != null;
    final E element = l.item;
    final Node<E> prev = l.prev;
    l.item = null;
    l.prev = null; // help GC
    last = prev;
    if (prev == null)
        first = null;
    else
        prev.next = null;
    size--;
    modCount++;
    return element;
}

unlink(Node x)
deletes the specified node, provided that the node cannot be null.

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

3. [Check]

node(int index)
This method is to obtain the target node according to the specified index . But what is different from an array is the time complexity. The time complexity of data obtaining elements based on subscripts is O(1), while the time complexity of obtaining targets based on subscripts in linked lists is O(n/2); because the known conditions are the first pointer and tail pointer maintained in the linked list, To find the element of the target index, you must know its address. The address of the linked list is uncertain, so it needs to be indexed sequentially from the beginning or the end until the target address is found.

Node<E> node(int index) {
    // assert isElementIndex(index);

    if (index < (size >> 1)) {		// size >> 1  意思为除以2,index与中间值比较,从而来选择从头开始还是从尾开始更快。
        Node<E> x = first;   		// 定义一个空结点,指向首结点。
    	
        for (int i = 0; i < index; i++)
            x = x.next;				 	// 通过每个结点的依次传递,找到index的结点
        return x;
    } else {
        Node<E> x = last;				// 定义一个空结点,指向尾结点。
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

Four, iterator

The LinkedList's ListIterator iterator is not much different from the ArrayList's ListIterator. It also provides two convenient traversals, forward and backward, and provides operations such as addition, deletion, and modification.

to sum up

The above is the core source code analysis of LinkedList, and other APIs are built on these core operations. The core source code includes additions, deletions, and checks. They are all basic operations on linked lists. In the whole process of understanding, you only need to pay attention to the first and last pointers maintained in the linked list, size and modCount, and iterators. Understand the principle of doubly linked list, these operations are very simple.

Guess you like

Origin blog.csdn.net/weixin_43901067/article/details/104989152