Preface
In the last article, we did a detailed analysis of ArrayList. Today, let's talk about LinkedList. What's the difference between them? The biggest difference is that the implementation of the underlying data structure is different. ArrayList is implemented as an array (see the previous article), and LinedList is implemented as a linked list. As for other differences, it can be said that most of them are due to different applications derived from different essentials.
LinkedList
linked list
Before analyzing LinedList, I will give a brief introduction to linked list. After all, linked list is not used as much as array, so it is inevitable that many people are not familiar with it.
Linked list is a basic linear data structure, which is linear with array, but array is linear in physical storage of memory and in logic, while linked list is only linear in logic. In each storage unit of the linked list, not only the current element is stored, but also the address of the next storage unit, so that all storage units can be connected together by address.
Every time you look up, you can find the elements you need through the first storage unit. To perform a deletion operation, you just need to disconnect the pointing of the relevant elements. The schematic diagram is as follows:



Of course? LinkedList is not a basic one-way list, but a two-way list.
There is a basic storage unit in the LinedList, which is an internal class of LinkedList. There are two attributes in the node element, and the references of the former node and the latter node are saved respectively.
//Static inner class private static class Node<E> { //Attributes of storage elements E item; //Front and back node references Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
Definition
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
Definition is similar to Array List, but it should be noted that LinkedList implements Deque (indirectly implements Qeque interface), which is a two-way pair of columns, providing a way for LinedList to access elements from both ends of the column.
Initialization
When we analyze ArrayList, we know that the initialization length of ArrayList is 10 when using parametric-free construction method, and all parametric-free constructed collections point to the same object array (static constants, located in the method area). So what is the initialization of LinkedList?
Open the parametric-free construction method
public LinkedList() { }
Nothing, then we can only look at the attributes.
//Initialization length is 0 transient int size = 0; //There are front and back nodes transient Node<E> first; transient Node<E> last;
Graphic Initialization
LinkedList<String> list = new LinkedList<String>(); String s = "sss"; list.add(s);

Method
add(E e)
public boolean add(E e) { linkLast(e); return true; }
From the method, we know that after calling the add method, we do not add it immediately, but call the linkLast method. See the name, the new element is added at the end of the collection.
void linkLast(E e) { // Assigning (referencing) the last element to the node l final Modifier-modified attributes cannot be changed after assignment final Node<E> l = last; // Invoke the node's parametric constructor to create a new node to save the added elements final Node<E> newNode = new Node<>(l, e, null); //At this point, the new node is the last element to assign the new node to last last = newNode; //If l yes null That means it's the first time you add elements, so first Assignment to the new node list There is only one element that stores the same element for both the beginning element and the last element. if (l == null) first = newNode; else //If it's not the first addition, assign the new node to l(The last element before adding) next l.next = newNode; //length+1 size++; //Modification times+1 modCount++; }
From the above code, we can see that it does not depend on subscripts when adding elements.
The process is to save the information of the last node (actually the last node) through a last (Node object) and add elements by changing the last element every time. (To fully understand this, you need to understand the difference and essence between java value passing and reference passing.)
add(int index, E element)
Add to the specified location public void add(int index, E element) { //Subscript Cross-Border Inspection checkPositionIndex(index); //If you add a direct call to the last linkLast if (index == size) linkLast(element); //Conversely calling linkBefore else linkBefore(element, node(index)); } //Insert the element before specifying the element void linkBefore(E e, Node<E> succ) { // assert succ != null; Hypothetical assertion succ Not for null //Define a node element save succ Of prev Reference is the information of its previous node. final Node<E> pred = succ.prev; //Create a new node element as the element to insert e prev Quote is pred That's before insertion. succ The first element next yes succ final Node<E> newNode = new Node<>(pred, e, succ); //here succ The last node is the insertion of a new node, so modify the node pointing succ.prev = newNode; // If pred yes null That means this is the first element. if (pred == null) //Member attribute first Point to a new node first = newNode; //Conversely else //Prenode element next Attributes point to new nodes pred.next = newNode; //length+1 size++; modCount++; }
Node element insertion diagram


In the code above, we should note that LinkedList inserts elements with certain validation, that is, subscript cross-border validation. Let's take a look at the implementation.
private void checkPositionIndex(int index) { if (!isPositionIndex(index)) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } //If input index Return within range ture private boolean isPositionIndex(int index) { return index >= 0 && index <= size; }
Through the analysis of the two adding methods, we can clearly feel the efficiency of adding elements to LinkedList, without expansion, without copying arrays.
get
public E get(int index) { //Checking whether the subscript element exists is actually checking whether the subscript is out of bounds. checkElementIndex(index); //Returns the corresponding subscript node if it does not cross the boundary item That's the corresponding element. return node(index).item; } //Subscript crossing check throws an exception if crossing the boundary private void checkElementIndex(int index) { if (!isElementIndex(index)) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } private boolean isElementIndex(int index) { return index >= 0 && index < size; } //This method is used to return non-empty nodes with specified Subscripts Node<E> node(int index) { //Assuming that the subscript has not crossed the boundary, it has not actually crossed the boundary. After all, the subscript crossing check has been carried out before that. // assert isElementIndex(index); //If index less than size One-half of all searches started in the past (backwards) and looked forward instead. if (index < (size >> 1)) {//Left shift efficiency is worth learning Node<E> x = first; //ergodic for (int i = 0; i < index; i++) //Each node's next It's the same time that his latter node references traversal x The next element assigned to the node traverses through the index It's what you get. index Elements of corresponding nodes x = x.next; return x; } else { Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }
This code fully embodies the advantages of the two-way linked list. It can be traversed from the past to the future. It can significantly improve the efficiency by judging the index range. But it is also obvious that the LinkedList get method is inefficient and time complexity O(n) when traversing.
remove(int index)
The so-called deletion node is to set the front and back references of the node to null, and ensure that no other node points to the deleted node.
public E remove(int index) { //Subscript Cross-Border Inspection checkElementIndex(index); //The return value here actually executes two methods //node Acquire subscript-setting non-empty nodes //unlink Disconnect the specified node return unlink(node(index)); } E unlink(Node<E> x) { //hypothesis x No null // assert x != null; //Define a variable element accept x Elements in a node will eventually return a value final E element = x.item; //Define the number of nodes to be obtained separately x Node references before and after nodes final Node<E> next = x.next; final Node<E> prev = x.prev; //If the reference before the node is null Explain that this is the first node if (prev == null) { //x The first node is about to be deleted. first Need to be reassigned first = next; } else { //If not x Not the first node will prev(x The previous node) next point x The latter node (bypass) x) prev.next = next; //x Pre-reference assignment null x.prev = null; } //If the node is referenced after the null Explain that this is the last node in a series of similar preceding references. if (next == null) { last = prev; } else { next.prev = prev; x.next = null; } //take x Element assignment in nodes null x.item = null; size--; modCount++; return element; }
Explain
- prev, item, next are null for virtual machine recycling
- We can see that LinkedList is also very efficient in deleting elements.
LinkedList Summary
- The query speed is not good. Every search requires traversal. This is the sequential subscript traversal mentioned in ArrayList analysis.
- Adding elements and deleting them all have speed advantages.
- Implementing Column-to-Column Interface
The difference between ArrayList and LinkedList
- Sequential insertion, both of which are fast, but ArrayList is slightly faster than LinkedList, which implements arrays that are created ahead of time; LinkedList needs to re-create new nodes every time.
- LinedList needs to maintain the front and back nodes, which will consume more memory.
- LinedList is suitable for iteration traversal; ArrayList is suitable for iteration traversal.
- Do not traverse LinedList with a normal for loop
- Also, don't use iteration to traverse ArrayList (see the article "ArrayList Analysis").
- Delete and insert are not necessary, after all, ArrayList needs to replicate arrays and expand.
I can't guarantee that everything is right, but I can guarantee that every sentence, every line of code is deliberated. I hope that behind every article is my attitude of pursuing pure technical life.
Always believe that good things are about to happen.