List of collections under source code

Posted by Sharif on Wed, 29 Dec 2021 02:15:05 +0100

Minor sequence:

First of all, there is a question, why do you choose collections? What convenience does its birth provide for programmers?

To clarify this, we have to start with the array; When guests come to our house, they usually take out fruits to entertain, but they don't just take one each time. Go and get it after eating? That would be too troublesome and impolite! We usually take a plate to hold fruit. Just enjoy the guests. This plate is equivalent to an array, which is used to carry data.

But the array is not perfect. It can only add the same type every time it adds data, and the size is fixed. Put it on the plate, that is, it can only contain one kind of fruit at a time, and the number is limited! People who have fewer plates at home are a little stretched.

But these problem sets can be easily solved. There is only one plate at home. No problem. You can put a variety of fruits on one plate. What? What if the plate is not big enough? It doesn't matter. Just tell me how many fruits you want. I can expand my capacity to hold all the fruits! How did you do that? I think the source code can do it 😄

Text:

ArrayList

Underlying data structure: array

Default initial capacity: 0

Capacity expansion: current array length * 1.5

Thread safe: unsafe

Features: fast query, slow addition and deletion

Next, we explain the above features from the source code

  • DemoTest class
    package com.zhao.Collection;
    
    import java.util.ArrayList;
    
    @SuppressWarnings({"all"})   //Suppress warning messages
    public class ArrayListTest {
        public static void main(String[] args) {
    
            ArrayList arrayList = new ArrayList();  //Create array
    
            for (int i = 1; i <= 10; i++) {
                
                arrayList.add(i);       //Add data 1 ~ 10
            }
    
            for (int i = 11; i <= 15; i++) {
                
                arrayList.add(i);       //Add data 11 ~ 15
            }
    
            arrayList.add(100);
            arrayList.add(200);
            arrayList.add(null);
    
        }
    }
    
  • ArrayList parameterless construction part of the source code
    package java.util;
    
    import java.util.function.Consumer;
    import java.util.function.Predicate;
    import java.util.function.UnaryOperator;
    import sun.misc.SharedSecrets;
    
    
    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
    
        //Used to verify the consistency of versions
        private static final long serialVersionUID = 8683452581122892189L;
    
        //Default initial capacity: 10
        private static final int DEFAULT_CAPACITY = 10;  
    
        //(parameterized construction) empty array
        private static final Object[] EMPTY_ELEMENTDATA = {};
    
        //(parameterless construction) default empty array
        private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
        //Element data. Variables modified by the transient keyword are not serialized or deserialized
        transient Object[] elementData;
    
        //Array length
        private int size;
    
        //Nonparametric structure
        public ArrayList() {
            //Initialize the collection and assign an empty array to the collection
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
    
    }
    
  • At this time, the collection has been initialized, and the collection length is 0. When the add (integer i) method is executed for the first time, the source code is as follows
    package java.util;
    
    import java.util.function.Consumer;
    import java.util.function.Predicate;
    import java.util.function.UnaryOperator;
    import sun.misc.SharedSecrets;
    
    
    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
    
        //Used to verify the consistency of versions
        private static final long serialVersionUID = 8683452581122892189L;
    
        //Default initial capacity: 10
        private static final int DEFAULT_CAPACITY = 10;  
    
        //(parameterized construction) empty array
        private static final Object[] EMPTY_ELEMENTDATA = {};
    
        //(parameterless construction) default empty array
        private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
        //Element data. Variables modified by the transient keyword are not serialized or deserialized
        transient Object[] elementData;
    
        //The maximum value of int is 2147483639
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
        //Array length
        private int size;
    
        //Nonparametric structure
        public ArrayList() {
    
            //Initialize the collection and assign an empty array to the collection
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        }
    
    
    
        //Add element for the first time
        public boolean add(E e) {
            
            //At this time, the default size is 0; (0+1 = 1 )
            ensureCapacityInternal(size + 1);  //The capacity expansion is realized by calling the ensureCapacityInternal method
            
            //Add an element to the 0 index in the array (size + +, after + +, the size is still 0)
            elementData[size++] = e;
    
            //Add successfully, return true
            return true;
        }
    
        //Ensure sufficient collection capacity
        private void ensureCapacityInternal(int minCapacity) {
            
            //Parameter 1: elementData initialized {}
            //Parameter 2: minCapacity is the minimum required capacity. At this time, only one element is added, so the value is 1;
            ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
        }
    
        
        //See how much capacity the collection requires at least
        private static int calculateCapacity(Object[] elementData, int minCapacity) {
            
            //  {} = {} is true, and the method knows that it is the first time to add an element
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    
                //DEFAULT_ The default value of capacity is 10 and the minCapacity value is 1 It has been analyzed above
                //Returns the largest of the two numbers. ten
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            }
    
            //Returns the minimum capacity value
            return minCapacity;
        }
    
        //Determine whether capacity expansion is required
        //Parameter: at this time, the reference value of the minimum capacity is 10, minCapacity = 10;
        private void ensureExplicitCapacity(int minCapacity) {
    
            modCount++;  //Modification times
    
            //elementData.length = 0ï¼› (because it is an empty collection)
            //If 10 - 0 > 0, the grow() method is called and the minCapacity value is passed into the method
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
    
    
        //Real collection expansion operation
        private void grow(int minCapacity) {
            
            //Record the length of the set. At this time, the set is empty, the length is 0 by default, and oldCapacity = 0;
            int oldCapacity = elementData.length;
            
            //oldCapacity > > 1, shift the oldCapacity to the right one bit, which is equivalent to 0 / 2 ^ 1,
            //oldCapacity > > 2. Move oldCapacity to the right by two bits, which is equivalent to 0 / 2 ^ 2.
            //Parameter newcapacity = 0;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            
            //If 0 - 10 < 0
            if (newCapacity - minCapacity < 0)
                
                // newCapacity = 10;
                newCapacity = minCapacity;
    
            //If 0 - 2147483639 > 0
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                
                //Is equal to your maximum
                newCapacity = hugeCapacity(minCapacity);
            
            //Copy the elements in the elementData array to the new capacity array, even if it has nothing, so as to complete the capacity expansion
            //At this time, elementData is 10 zeros;
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    
    
        private static int hugeCapacity(int minCapacity) {
            
            //If the required capacity is less than 0
            if (minCapacity < 0) 
                
                //Throwing anomaly
                throw new OutOfMemoryError();
            
            //Otherwise, the maximum value between two is returned
            return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
        }
    
    
    }
    
    
    

  • Method, add elements -- > to check whether capacity expansion is needed -- > to see the minimum capacity required for this addition -- > if the collection capacity is insufficient, expand the capacity. If that's enough, go back and add. Capacity expansion is to add 1.5 to the current capacity for capacity expansion (except for the elements added for the first time / longer than the collection len gt h). Finally, the old elements are copied to the new array, and the reference remains unchanged.

  • When you add the second element, you have finished judging whether to expand the capacity

            //elementData.length = 10ï¼› (because it has been expanded for the first time)
            //If 2 - 10 > 0, the grow() method is called and the minCapacity value is passed into the method
            //Now it is not greater than 0. If it is determined that your array capacity is sufficient, you can directly add elements and return
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);

The second expansion is triggered by adding the eleventh element, that is, when the index coordinate is 10. Now the index is only 0 to 9. When adding the tenth index element, there is no place to put it, so the second expansion has to be carried out. There will be no demonstration here!

The above ArrayList features have been basically demonstrated, and thread safety and why query speed are not involved. Why its query is fast will be demonstrated together with LinkedList. Now let's talk about why its thread is unsafe!

DemoTest

package com.zhao.Collection;

import java.util.ArrayList;
import java.util.List;

@SuppressWarnings({"all"})
public class ArrayListInThread implements Runnable {

    //Thread unsafe
    private List threadList = new ArrayList();

    @Override
    public void run() {
        try {

            //Sleep for 10 milliseconds first to avoid running too fast, so that there will be no snatching the execution right of the cup
            Thread.sleep(10);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //Add the current thread name to the ArrayList (this step is very important, and the error points are here)
        threadList.add(Thread.currentThread().getName());
    }

    public static void main(String[] args) throws InterruptedException {

        //Create the current class (because it implements the Runnable interface, I now want to start the thread and call the run method inside it)
        ArrayListInThread listThread = new ArrayListInThread();

        for (int i = 0; i < 100; i++) {
            
            //Give each thread a name, starting at 0
            Thread thread = new Thread(listThread, String.valueOf(i));

            //Open thread
            thread.start();
        }

        //Wait for the child thread to finish executing
        Thread.sleep(2000);

        //Length of output set
        System.out.println(listThread.threadList.size());

        //Output values in ArrayList
        for (int i = 0; i < listThread.threadList.size(); i++) {

            //If there is a null value in the collection, it will be output on a new line (the following will explain why null values appear back)
            if (listThread.threadList.get(i) == null) {
                System.out.println();
            }

            //Prints each value in the set
            System.out.print(listThread.threadList.get(i) + " ");
        }
    }
}

What may happen to the console:

First:

There are only 98 elements in the collection, which should normally be 100. Where are the other two? This is easy to understand. Looking at the source code, we can see that the operation of "elementData[size++] = e" is composed of three steps:

  1. elementData[size] = e
  2. size = sizeï¼›// Size indicates the length of the array
  3. size = size + 1

Thread AThread B
size = 30
elementData[size], add element at
size = 30
elementData[size], overwrite element at
size = 30;
size = 30;
size = 30 + 1;
size = 30 + 1;
size = 31 (came over and made a soy sauce)size = 31 (all think they added it by themselves)

The second case:

A null value appears. How does this null value appear?

Thread AThread B
size = 31
elementData[size], add element at
size = 31
elementData[size], overwrite element at
size = 31;
size = 31 + 1;
size = 32;
size = 32 +1;
size = 32size = 33

There are already elements at index 31, and thread B overwrites and adds them. Thread A thinks it has added them. The set length is + 1, (I don't want to be overwritten by thread b). After thread B completes the coverage, the set length is 32. Because there is no lock, it won't make A judgment similar to optimistic lock, so it will directly + 1 on the set length. As A result, the position of index 32 is not related to the element, resulting in the embarrassment of null value!

The third case:

Familiar exception > index out of bounds. How did this happen?

In fact, it is still related to capacity expansion

Thread AThread B
Add 10th elementAdd 10th element
if (minCapacity - elementData.length > 0)
            grow(minCapacity);
10 - 10 > 0 - false, no capacity expansion
Call elementData[size++] = e; Method. Please note that you have just come here and haven't added it yet
Go through the process of thread A again and judge that there is no need to expand the capacity
elementData[10], add element at
size++ï¼›size = 11
elementData[11], add element at
Finally, the index is out of bounds! But this does not affect the judgment of the next thread

It can be seen that ArrayList set merging is not safe, and safe sets are used in multiple concurrent cases. For example, the Vector to be discussed below

                                                

                                               

Vector

Underlying data structure: array

Default initial capacity: 10

Capacity expansion: current array length * 2

Thread safety: Security

Features: it has no advantages except safety

Explain these features from the source code

  • DemoTest class
    package com.zhao.Collection;
    
    import java.util.Vector;
    
    public class VectorTest {
    
        public static void main(String[] args) {
    
            Vector vector = new Vector();    //Create array
    
            for (int i = 1; i <= 10; i++) {
    
                vector.add(i);     //Add data 1 ~ 10
            }
            
            vector.add(100);       //Capacity expansion will be triggered here
            
            vector.add(200);
        }
    
    
    }
  • Vector parameterless construction part of the source code
    package java.util;
    
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.StreamCorruptedException;
    import java.util.function.Consumer;
    import java.util.function.Predicate;
    import java.util.function.UnaryOperator;
    
    
    public class Vector<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
        
    
        //Empty array (used for initialization)
        protected Object[] elementData;
    
        
        //Number of elements
        protected int elementCount;
    
        
        //Self increasing capacity
        protected int capacityIncrement;
    
        
        //Parametric structure
        public Vector(int initialCapacity, int capacityIncrement) {
            super();
            
            //Judge whether the value passed in is less than 0
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal Capacity:initialCapacityv");
    
            //Initialize the empty array. The default initial capacity is 10
            this.elementData = new Object[initialCapacity];
    
            //Assign self increasing capacity
            this.capacityIncrement = capacityIncrement;
        }
    
        
        //Parameter structure. The initial capacity is the value passed in by the user
        public Vector(int initialCapacity) {
            
            this(initialCapacity, 0);
        }
    
       
        //Null parameter structure, the default initial capacity is 10
        public Vector() {
            this(10);
        }
    
    }
    
  • At this time, the collection has been initialized, and the collection length is 0. When the add (integer i) method is executed for the first time, the source code is as follows
    package java.util;
    
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.StreamCorruptedException;
    import java.util.function.Consumer;
    import java.util.function.Predicate;
    import java.util.function.UnaryOperator;
    
    
    public class Vector<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
        
    
        //Empty array (used for initialization)
        protected Object[] elementData;
    
        
        //Number of elements
        protected int elementCount;
    
        
        //Self increasing capacity
        protected int capacityIncrement;
    
        
        //Parametric structure
        public Vector(int initialCapacity, int capacityIncrement) {
            super();
            
            //Judge whether the value passed in is less than 0
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal Capacity:initialCapacity");
    
            //Initialize the empty array. The default initial capacity is 10
            this.elementData = new Object[initialCapacity];
    
            //Assign self increasing capacity
            this.capacityIncrement = capacityIncrement;
        }
    
        
        //Add element
        public synchronized boolean add(E e) {
    
            modCount++;   //Modification times
            
            //Ensure small capacity assistant
            //The parameter elementCount defaults to 0, which is an empty collection
            ensureCapacityHelper(elementCount + 1);
    
            elementData[elementCount++] = e;
    
            return true;
        }
        
    
        //Ensure that the capacity must be sufficient. minCapacity:1
        private void ensureCapacityHelper(int minCapacity) {
            
            //If 1 - 10 > 0
            if (minCapacity - elementData.length > 0)
    
                //Capacity expansion method
                grow(minCapacity);
        }
    
    
        //For the capacity expansion method, minCapacity = 11 when running here for the first time
        private void grow(int minCapacity) {
            
            //The array length is assigned to oldCapacity = 10
            int oldCapacity = elementData.length;
    
            //capacityIncrement is always 0, which is defined by the Vector designer. 0 > 0 is always true
            //The new capacity is the sum of the two old capacities
            int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                                capacityIncrement : oldCapacity);
    
            //If the newly expanded capacity - minimum required capacity < 0
            if (newCapacity - minCapacity < 0)
    
                //The minimum required capacity is assigned to the new capacity
                newCapacity = minCapacity;
    
            //If the maximum value range of the newly expanded capacity - Integer is > 0
            if (newCapacity - MAX_ARRAY_SIZE > 0)
    
                //Assign the maximum capacity to the new capacity
                newCapacity = hugeCapacity(minCapacity);
    
            //Copy all elements from the old array to the new array and point to the old reference
            elementData = Arrays.copyOf(elementData, newCapacity);
    
        }
        
        private static int hugeCapacity(int minCapacity) {
    
            //If the minimum required capacity is < 0
            if (minCapacity < 0) 
                
                //Throwing anomaly
                throw new OutOfMemoryError();
    
            //Returns the maximum of two
            return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
        }
    
    }
    

After reading the source code of Vector, I feel that the two sets are not written by the same person. The ArrayList design is a little more rigorous than Vector. But Vector has less code and looks more elegant!

The first expansion of Vector is triggered when the eleventh element is added. Moreover, it uses a condition equal to false to ensure that each expansion is the addition of two old sets. That is, double the capacity.

It is thread safe because its methods are decorated with synchronized. So its performance is also very low. Its thread safety will not be demonstrated here.

LinkedList

Underlying data structure: bidirectional linked list

Default initial capacity: 0

Capacity expansion: no capacity expansion

Thread safe: unsafe

Features: fast addition and deletion, slow query

Next, we explain the above features from the source code

  • DemoTest class
    package com.zhao.Collection;
    
    import java.util.LinkedList;
    
    @SuppressWarnings({"all"})
    public class LinkedListTest {
    
        public static void main(String[] args) {
    
            //Create collection
            LinkedList linkedList = new LinkedList();
    
            for (int i = 1; i <= 100; i++) {
    
                //Add element
                linkedList.add(i);
            }
    
            //modify
            linkedList.set(70,999);
            System.out.println("Modified LinkedList: "+linkedList);
    
            //query
            Integer elementBy70 = (Integer) linkedList.get(70);
            System.out.println("LinkedList Element at index 70: "+elementBy70);
    
            //delete
            linkedList.remove();
            System.out.println("After deleting an element LinkedList: "+linkedList);
    
        }
    }
    
  • LinkedList parameterless construction part of the source code
    public class LinkedList<E> extends AbstractSequentialList<E>
                   implements List<E>, Deque<E>, Cloneable,java.io.Serializable{
    
    
        //The collection length is 0 by default
        transient int size = 0;
    
        //Head node
        transient Node<E> first;
    
        //Tail node
        transient Node<E> last;
    
        //Empty parameter structure
        public LinkedList() {
        }
        
    
        //Inner class
        private static class Node<E> {
            
            //Node element (the object you add when you add)
            E item;
    
            //Address of the next node
            Node<E> next;
    
            //Address of the previous node
            Node<E> prev;
    
            //Parameterized construction to assign values
            Node(Node<E> prev, E element, Node<E> next) {
                this.item = element;
                this.next = next;
                this.prev = prev;
            }
    
        }
    
    }
    
    
    
  • When the add (integer i) method is executed for the first time, the source code is as follows

    public class LinkedList<E> extends AbstractSequentialList<E>
                   implements List<E>, Deque<E>, Cloneable,java.io.Serializable{
    
    
        //The collection length is 0 by default
        transient int size = 0;
    
        //Head node
        transient Node<E> first;
    
        //Tail node
        transient Node<E> last;
    
        
        //Add element
        public boolean add(E e) {
        
            //Call the method to add the tail node
            linkLast(e);
            
            return true;
        }
    
        
        //Tail node append
        void linkLast(E e) {
            
            //At this time, last is uninitialized and null. Assign the null value to the node l
            final Node<E> l = last;
    
            //Create a new Node. The Node has parameters. The three parameters are the previous Node, the added element and the next Node
            //The previous node of the new node points to l
            final Node<E> newNode = new Node<>(l, e, null);
    
            //Assign the new node to the last variable (record it, which is the head node address of the next node)
            last = newNode;
    
            //If l == null
            if (l == null)
    
                //Assign the new node to the first variable
                first = newNode;
    
            else
                l.next = newNode;
            
            //Set length + 1
            size++;
    
            //Modification times + 1
            modCount++;
        }
        
    
        //Inner class
        private static class Node<E> {
            
            //Node element (the object you add when you add)
            E item;
    
            //Address of the next node
            Node<E> next;
    
            //Address of the previous node
            Node<E> prev;
    
            //Parameterized construction to assign values
            Node(Node<E> prev, E element, Node<E> next) {
                this.item = element;
                this.next = next;
                this.prev = prev;
            }
    
        }
    
    }
    
    

  • The add method in LikendList is exquisitely designed, just a little around. I suggest you run the source code again, so that you can deeply understand it

The following compares the query and deletion of ArrayList and LinkedList through the source code

ArrayList has fast queries and slow additions and deletions

LinkedList has slow queries and fast additions and deletions

ArrayList query and delete source code

package java.util;

import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import sun.misc.SharedSecrets;


public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{


    //Element data. Variables modified by the transient keyword are not serialized or deserialized
    transient Object[] elementData;

    //Array length
    private int size;


    //Query & & get
    public E get(int index) {

        //Check index
        rangeCheck(index);

        //Returns the element corresponding to the index from the array
        return elementData(index);
    }


    //delete
    public E remove(int index) {

        //Check index
        rangeCheck(index);

        //Modification times + 1
        modCount++;

        //Gets the corresponding element at index
        E oldValue = elementData(index);

        //Array length - index value - 1 to determine whether the last element is deleted
        int numMoved = size - index - 1;

        //If it is true, it indicates that the last bit is not deleted, and the element position in the array needs to be readjusted
        if (numMoved > 0)

            //Call local methods to adjust the position of elements in the array
            System.arraycopy(elementData, index+1, elementData, index, numMoved);

        
        //Set the position of the last bit in the array length to null
        elementData[--size] = null; // Let GC do its cleanup

        //Return old value
        return oldValue;
    }


    //Get element
    E elementData(int index) {

        //Gets the element at index
        return (E) elementData[index];
    }

    
    //Check whether the index is legal
    private void rangeCheck(int index) {
        
        //If the index is greater than or equal to the array length
        if (index >= size)

            //Throw exception
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }


    //Console exception information
    private String outOfBoundsMsg(int index) {

        //Printed character information
        return "Index: "+index+", Size: "+size;
    }

}

It can be seen from the source code that the query speed of Arraylist is really fast. According to the corner mark query, which element you need can be directly located on the required element. But its deletion efficiency is not satisfactory, if it is not to delete the last element. It also needs to call local methods to copy the elements in the array, and then fill in the missing index. Consume more performance!

LinkedList query and delete source code

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


    //The collection length is 0 by default
    transient int size = 0;

    //Head node
    transient Node<E> first;

    //Tail node
    transient Node<E> last;

   
    //Get value from index
    public E get(int index) {
        
        //Index check
        checkElementIndex(index);
        
        //Call the node method to get the value of the index position
        return node(index).item;
    }

       
    //Element at index position
    Node<E> node(int index) {
        
        //If the index is less than half the length of the collection
        if (index < (size >> 1)) {

            //Assign the first node address to the variable x
            Node<E> x = first;

            //The traversal starts from the first node until the end of the previous element of the index
            for (int i = 0; i < index; i++)
                
                //Assign the address value where the index is located to the x node
                x = x.next;

            //Returns the node element corresponding to the index
            return x;

        } else {
            
            //Assign the last node to the variable x
            Node<E> x = last;

            //The traversal starts from the last node until the end of the next element after the index
            for (int i = size - 1; i > index; i--)

                //Assign the address value where the index is located to the x node
                x = x.prev;

            //Returns the node element corresponding to the index
            return x;

        }
    }  


    //Check index
    private void checkElementIndex(int index) {

        //If it is legal and the inverse is false, the method is not entered
        //Illegal. If it is reversed to true, an exception will be thrown
        if (!isElementIndex(index))

            //Throw index out of bounds exception
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    
    //Determine whether the index is within the range of the set
    private boolean isElementIndex(int index) {
        
        //Returns the Boolean value of index > = 0 & & index < size
        return index >= 0 && index < size;
    }

    
    //Abnormal information
    private String outOfBoundsMsg(int index) {

        //Character information printed by console
        return "Index: "+index+", Size: "+size;
    }



    //Removing Elements 
    public E remove() {
        
        //Remove first by default
        return removeFirst();
    }

    
    //Remove first element
    public E removeFirst() {

        //Assign the header node to the variable f
        final Node<E> f = first;

        //If the header node is equal to null
        if (f == null)
            
            //Throw exception
            throw new NoSuchElementException();

        //Call the method to remove the header node
        return unlinkFirst(f);

    }  


    //Remove header node
    private E unlinkFirst(Node<E> f) {

        //Get the element of the header node
        final E element = f.item;

        //Get the next node address of the head node
        final Node<E> next = f.next;

        //Set the element of the header node to null
        f.item = null;

        //Set the address of the header node pointing to the next node to be empty, and the empty object waits for GC recycling
        f.next = null; // help GC

        //Assign the next node address obtained in the previous two steps to the member variable header node
        first = next;

        //If the next node is empty (there is only one element in the collection, that is, the element just deleted)
        if (next == null)

            //Set the member variable last to null
            last = null;

        else
            
            //Set the address of the next node of the current header node to null (wait for the next added node to complete it)
            next.prev = null;

        //Set length minus 1
        size--;

        //Modification times + 1
        modCount++;

        //Returns the deleted header node
        return element;
    }


    //Removes the element at the specified index
    public E remove(int index) {
    
        //Check index
        checkElementIndex(index);

        //Remove the node. The node(index) method obtains the corresponding node at the index first
        return unlink(node(index));
    }


    //Remove node
    E unlink(Node<E> x) {
        
        //Gets the element of the incoming node
        final E element = x.item;

        //Get the address value of the next node
        final Node<E> next = x.next;

        //Get the address value of the previous node
        final Node<E> prev = x.prev;
    
        //If the previous address value is null, (the first node is deleted)
        if (prev == null) {

            //Make the next node the first node. The prev of the next node will disappear with the GC cleared
            first = next;

        } else {
    
            //Connect the address value of the next node of the element to be deleted with the next attribute of the previous node
            prev.next = next;
            
            //After the handover is ready, set the address value of the last node of the deleted element to null
            x.prev = null;
        }

        //If the address value of the next node of the deleted node is null, the last element
        if (next == null) {

            //Set the address value of the previous node to the last element
            last = prev;

        } else {
            
            //Connect the address value of the previous node of the element to be deleted with the prev attribute of the next node
            next.prev = prev;

            //After the handover is ready, set the address value of the next node of the deleted element to null
            x.next = null;
        }

        //Set element to null
        x.item = null;
        
        //Set length--
        size--;

        //Modification times++
        modCount++;

        //Returns the deleted element
        return element;
    }



    //Inner class
    private static class Node<E> {
        
        //Node element (the object you add when you add)
        E item;

        //Address of the next node
        Node<E> next;

        //Address of the previous node
        Node<E> prev;

        //Parameterized construction to assign values
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }

    }

}

It can be seen from the source code that the addition and deletion of LinkedList is fast as long as the index is not specified. It does not need to copy elements like ArrayList and add them to the collection again. Saves copy overhead. But its query speed is really moving. Although the dichotomy is used for optimization in the source code, it still needs to traverse to the index position one by one. Then take out the elements in the node.

In view of the nodes around in the LinkedList source code, in the future, as long as the array does not involve header insertion, choose ArrayList. Vector can be selected for thread safety. LinkedList thread is not safe. Vector is still used for thread safety, although it is not recommended in JDK.

Topics: Java