An overview of the
LinkedList and ArrayList are two different implementations of the List interface. The efficiency of adding and deleting a List is low, but the efficiency of changing a List is high. On the contrary, the addition and deletion of LinkedList is more efficient because it does not need to move the underlying array data, which is realized by LinkedList and only needs to modify the LinkedList node pointer.
However, both modification and lookup need to locate the target node first (because null values are allowed, two for loops are often traversed, with time complexity o(n)), so the efficiency is low.
To begin, say collection.toarray () again; . This method is very important, both ArrayList and LinkedList will be converted into arrays when batch add. Because arrays can be traversed directly with a for loop. More convenient and efficient
In a nutshell, LinkedList is a thread-unsafe two-way list that allows elements to be null. The underlying data structure is a linked List, which implements the List
, Deque
, Cloneable, java.io.Serializable interface, which implements Deque
, so it can also be a double-ended queue. Compared to ArrayList, RandomAccess is not implemented, so it is slower to randomly access elements with subscripts.
As its underlying data structure is a linked list, it is conceivable that its addition and deletion only need to move the pointer, so the time efficiency is high. No batch expansion is required, and no reserved space is required, so the space efficiency is higher than ArrayList.
The disadvantage is that when random access is required, the time efficiency is very low. Although the bottom layer will judge whether the target Node is in the first half or the second half according to the index when querying Node according to the index, and then decide whether to query in order or reverse order to improve the time efficiency. However, as n increases, the overall time efficiency is still very low. ModCount is modified each time it is added or deleted.
A constructor
// The number of set elements
transient int size = 0;
// List header node
transient Node<E> first;
// The end of the list
transient Node<E> last;
// Do nothing
public LinkedList(){}Public Boolean addAll(Collection
c) Inserts all elements of collection C into a linked list
public LinkedList(Collection<? extends E> c) {
this(a); addAll(c); }Copy the code
Node Node structure:
private static class Node<E> {
E item;/ / element value
Node<E> next;// back node
Node<E> prev;// The front node
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, this is a two-way linked list.
Commonly used API
Increased 1.
AddAll Batch insert
//addAll, add a batch at the end
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);// Insert all elements in set C with size as insert subscript
}
// Insert all elements in c with index as index
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);// Check for out-of-bounds [0,size] closed interval
Object[] a = c.toArray();// Get the target collection array
int numNew = a.length;// The number of new elements
if (numNew == 0)// If the number of new elements is 0, it is not increased and false is returned
return false;
Node<E> pred, succ; // The index node is the front node and the back node
if (index == size) { // Append data to the end of the list
succ = null; //size must be null after the queue node
pred = last;// The front node is the end of the queue
} else {
succ = node(index);// Fetch the index node as the rear node
pred = succ.prev; // The front node is the node before the index node
}
// The list is added in batches by a for loop that iterates through the array and inserts nodes in sequence. ArrayList is added in batches through system. arrayCopy
for (Object o : a) {// Iterate over the nodes to be added.
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);// Build a new node with the preceding node and element value e,
if (pred == null) // If the front node is empty, it is a header
first = newNode;
else// Otherwise, the rear node of the front node is set to the new node
pred.next = newNode;
pred = newNode;// Step, the current node is the front node, ready for the next node to add
}
if (succ == null) {// After the loop ends, check if the trailing node is null. This indicates that the queue is at the end of the append.
last = pred; // Set the tail node
} else {
pred.next = succ; // If the node is inserted in the queue, update the front node and the back node
succ.prev = pred; // Update the front node of the rear node
}
size += numNew; // Change the quantity size
modCount++; / / modify modCount
return true;
}
// Select Node from index;
Node<E> node(int index) {
// assert isElementIndex(index);
// When retrieving a node by subscript, the index will be halved according to whether the index is in the first half or the second half to improve query efficiency
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;
}
}
private void checkPositionIndex(int index) {
if(! isPositionIndex(index))throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size; // Insert check, subscript can be size [0,size]
}
Copy the code
Insert a single node
// Insert a node at the end: add
public boolean add(E e) {
linkLast(e);
return true;
}
// Generate a new node and insert it at the end of the list to update the last/first node.
void linkLast(E e) {
final Node<E> l = last; // Record the original tail node
final Node<E> newNode = new Node<>(l, e, null);// Use the old tail node as the front node of the new node
last = newNode;// Update the tail node
if (l == null)// If the original list is empty, additional headers need to be updated
first = newNode;
else// Update the last node to the current last node (new node)
l.next = newNode;
size++;/ / modify the size
modCount++;/ / modify modCount
}
Copy the code
// Insert a node at index
public void add(int index, E element) {
checkPositionIndex(index);// check if the subscript is out of bounds [0,size]
if (index == size)// Insert after the last node
linkLast(element);
else// Insert in the middle
linkBefore(element, node(index));
}
// Insert a new node e before succ
void linkBefore(E e, Node<E> succ) {
// assert succ ! = null;
// Save the front node of the rear node
final Node<E> pred = succ.prev;
// Build a new node with the preceding and trailing nodes and element value e
final Node<E> newNode = new Node<>(pred, e, succ);
// The new node is the pre-node of the original node succ
succ.prev = newNode;
if (pred == null)// If the preceding node is empty, succ is the original header. So the new node is now the head node
first = newNode;
else// Otherwise, the post-node of the build front node is new
pred.next = newNode;
size++;// Change the number
modCount++;/ / modify modCount
}
Copy the code
Summary: Add-ons must modify modCount
Delete the remove
// Delete: remove target node
public E remove(int index) {
checkElementIndex(index);// check if subscript [0,size] is exceeded
return unlink(node(index));// Remove a node from the list
}
// Remove the x node from the list
E unlink(Node<E> x) {
// assert x ! = null;
final E element = x.item; // The element value of the current node
final Node<E> next = x.next; // The node after the current node
final Node<E> prev = x.prev;// The front node of the current node
if (prev == null) { // If the front node is empty (the current node was originally a head node)
first = next; // then the header is equal to the post-node
} else {
prev.next = next;
x.prev = null; // Empty the front node of the current node
}
if (next == null) {// If the last node is empty, the current node is the last node.
last = prev; // The last node is the front node
} else {
next.prev = prev;
x.next = null;// Empty the post-node of the current node
}
x.item = null; // Empty the current element value
size--; // Change the number
modCount++; / / modify modCount
return element; // Returns the value of the fetched element
}
private void checkElementIndex(int index) {
if(! isElementIndex(index))throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/ / the subscript [0, size)
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
Copy the code
Delete the specified element
// Because null elements are to be considered, it is also iterated twice
public boolean remove(Object o) {
if (o == null) {// If you want to delete a null node (as can be seen from remove and add, null elements are allowed)
// Iterate over each node comparison
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;
}
// Delete node x from the list
E unlink(Node<E> x) {
// assert x ! = null;
final E element = x.item;// Continue the element value for return
final Node<E> next = x.next;// Save the post-node of the current node
final Node<E> prev = x.prev;// The front node
if (prev == null) {// The front node is null,
first = next;// The first node is next
} else {// Otherwise update the rear node of the front node
prev.next = next;
x.prev = null;// Remember to set the preceding node of the node to null
}
// If the trailing node is null, it is the tail node
if (next == null) {
last = prev;
} else {// Otherwise update the front node of the back node
next.prev = prev;
x.next = null;// Remember to delete the node after the node is null
}
// Set the element value of the deleted node to null for GC
x.item = null;
size--;/ / modify the size
modCount++;/ / modify modCount
return element;// Returns the value of the deleted element
}
Copy the code
Deletion will also definitely modify modCount. Delete a Node by index and unlink the Node from the unlinked list. Delete by element: iterates through the list to see if the Node is present, and then unlinks it twice to allow null values.
To change the set
public E set(int index, E element) {
checkElementIndex(index); // Check for out-of-bounds [0,size]
Node<E> x = node(index);// Fetch the corresponding Node
E oldVal = x.item;// Save the old value for return
x.item = element;// Overwrite the old value with the new value
return oldVal;// Return the old value
}
Copy the code
ModCount = modCount (index)
Check the get
- Query nodes based on index
public E get(int index) {
checkElementIndex(index);[0,size]
return node(index).item; // Call the node() method to fetch the node node,
}
Copy the code
- Based on the node object, query subscript, also need to go through the linked list twice.
public int indexOf(Object o) {
int index = 0;
if (o == null) {// If the target object is null
// Iterate over the list
for(Node<E> x = first; x ! =null; x = x.next) {
if (x.item == null)
returnindex; index++; }}else {//// iterate through the linked list
for(Node<E> x = first; x ! =null; x = x.next) {
if (o.equals(x.item))
returnindex; index++; }}return -1;
}
Copy the code
- Iterate through the list from end to end to find the node whose target element value is O
public int lastIndexOf(Object o) {
int index = size;
if (o == null) {
for(Node<E> x = last; x ! =null; x = x.prev) {
index--;
if (x.item == null)
returnindex; }}else {
for(Node<E> x = last; x ! =null; x = x.prev) {
index--;
if (o.equals(x.item))
returnindex; }}return -1;
}
Copy the code
ModCount cannot be modified
toArray()
Let’s also take a look at toArray(). This is a high-frequency API, after all
public Object[] toArray() {
// Create a new array and then iterate over the list, storing each element in the array, return
Object[] result = new Object[size];
int i = 0;
for(Node<E> x = first; x ! =null; x = x.next)
result[i++] = x.item;
return result;
}
Copy the code
Conclusion:
LinkedList is a two-way list.
- Lists are added in batches by a for loop that iterates through the array and inserts nodes in turn. ArrayList is added in batches through system. arrayCopy. ModCount must be modified.
- When a node is obtained by subscript (Add select), the index is halved according to whether the index is in the first half or the second half to improve query efficiency
- Deletion will also definitely modify modCount. Delete a Node by index and unlink the Node from the unlinked list. Delete by element: iterates through the list to see if the Node is present, and then unlinks it twice to allow null values.
- Find Node based on index and replace the value. ModCount is not modified.
- Lookup itself is to find a Node based on index.
- So its CRUD operations are designed to find nodes by index.