Java Summary - List Implementation Class ArrayList&LinkedList

Keywords: Java Attribute

  • This article is based on the source code for learning, if I have any misunderstanding, please correct, thank you.

  • The above is basically the class diagram relationship of List collection classes. The tag interfaces such as Cloneable are omitted. Then the main implementation classes of List are: ArrayList,Vector,LinkedList,Stack. This article will introduce these four implementation classes (for space reasons, this article only talks about ArrayList and LinkedList).

ArrayList

  • This is the most commonly used implementation class of List. Then the storage of this class is realized by arrays. If it exceeds the specified threshold of arrays, it will be automatically expanded. In fact, automatic expansion is to copy array data into a new larger array to achieve the purpose of expansion. Let's look at some of the attribute source code of ArrayList.

    //Default capacity, which will be extended to DEFAULT_CAPACITY when the first element is added
    private static final int DEFAULT_CAPACITY = 10;
    //Shared empty array instance
    private static final Object[] EMPTY_ELEMENTDATA = {};
    //This is an array of stored data, non-private to simplify nested class access
    //The capacity of the array list is the length of the array array array.
    //When the first element is added, when elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA of any empty ArrayList is extended to DEFAULT_CAPACITY
    transient Object[] elementData;
    //Share empty array instances for default size empty instances
    //Distinguish it from EMPTY_ELEMENTDATA to see how much it adds to the first element
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    private int size;
    ...

Initialization

  • ArrayList provides three constructions, as follows

    public ArrayList() {...}
    public ArrayList(int initialCapacity) {...}
    public ArrayList(Collection<? extends E> c) {...}
  • Empty parameters: We introduce the construction method of empty parameters from the first ArrayList. Here is the implementation of the source code.

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
  • How do you understand that when the first element is added, the elementary data == DEFAULTCAPACITY_EMPTY_ELEMENTDATA of any empty ArrayList will be extended to DEFAULT_CAPACITY? When the ArrayList is empty and the two attribute values are equal, it means that the incoherent construct is called. When the construct is completed, it is not constructed as the default size, but when the first element is added. In addition, the condition of judgment is established that the values of two attributes are equal before the default size is extended.
  • So when we construct null parameter objects, the initial value array length is 0, not directly extended to an array length of 10, code validation

    public static void main(String[] args) throws Exception {
        ArrayList<String> arrayList = new ArrayList<>();
        getSize(arrayList);
        arrayList.add("x");
        getSize(arrayList);
    }
    private static void getSize(ArrayList<String> arrayList) throws Exception {
        Field elementData = arrayList.getClass().getDeclaredField("elementData");
        elementData.setAccessible(true);
        Object[] o = (Object[]) elementData.get(arrayList);
        System.out.println(o.length);
    }
    //0 10
    //When we create ArrayList with 0 as the initialization length parameter, we actually tell him that none of them exists, so he creates an array of 0 length, and when we add data, we automatically expand one.
    //Verification: The initialization can be changed to ArrayList < String > arrayList = new ArrayList <> (0);
    //Then the output is 0.1
    //So that's why we need to distinguish DEFAULTCAPACITY_EMPTY_ELEMENTDATA from EMPTY_ELEMENTDATA.
  • We can see from it that if we only initialize the Array List with empty parameters, then we only assign an empty array to the elementData attribute, so EMPTY_ELEMENTDATA is also an empty array object, what is it used for? It is only used to construct the Array LIst with empty parameters = 0. And DEFAULTCAPACITY_EMPTY_ELEMENTDATA is the object we use when we construct the empty parameter Array List, that is the case. It can be seen from the following analysis of another structure

    //Using EMPTY_ELEMENTDATA
    List<String> arrayList = new ArrayList<>(0);
    //Using DEFAULTCAPACITY_EMPTY_ELEMENTDATA
    List<String> arrayList = new ArrayList<>();
  • We also noticed that DEFAULTCAPACITY_EMPTY_ELEMENTDATA and EMPTY_ELEMENTDATA are both decorated with private static final, so these two empty arrays belong to classes and only one exists. This means that when you create two ArrayList s with a capacity of 0, they all point to an object in heap memory. Let's try it.

    public static void main(String[] args) throws Exception {
        ArrayList<String> list1 = new ArrayList<>();
        Field elementData1 = list1.getClass().getDeclaredField("elementData");
        elementData1.setAccessible(true);
        Object o1 = elementData1.get(list1);
        ArrayList<String> list2 = new ArrayList<>();
        Field elementData2 = list2.getClass().getDeclaredField("elementData");
        elementData2.setAccessible(true);
        Object o2 = elementData1.get(list2);
        System.out.println(o1 == o2);
    } //true
  • So ArrayList has actually thought about making a cache for our empty set, and when we add data to the empty set, elementData points to other objects. This is the source code scope of the add method. So in a moment, the first empty parameter construction method has already been introduced. Here is the source code implementation of the int parameter construction method.

    public ArrayList(int initialCapacity) {
      //Create arrays of specified length instead of 0
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
          //For 0, point directly to the created array
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
          //Illegal parameters
            throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
        }
    }
  • Needless to say, this is easy, and you can see why ArrayList has to design two empty arrays for use. There's nothing to say about this construction. So here's the source code for creating sets as parameters.

    //Converting parameters to ArrayList
    public ArrayList(Collection<? extends E> c) {
      //Because the toArray method is defined in Collection, his implementation classes will implement their own toArray, so you can call the return array directly without error.
        elementData = c.toArray();
        //If the length of the returned array is not an empty array
        if ((size = elementData.length) != 0) {
          //Prevent c.ToArray error from returning Object []
            if (elementData.getClass() != Object[].class)
            //Then convert elements in elementData to Object types
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            //So this is an empty array, so you can directly refer to the created empty array and save space.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
  • The way ArrayList is created is probably over the top, so the following ArrayList implementation method, I just pick a few core methods to look at the source code.

add

  • First of all, add. Let's use ArrayList to add elements, and then we read the code, so let's take a look at the source code.

    //Use
    ArrayList<String> arrayList = new ArrayList<>();
    arrayList.add("A");
    //Source Start
    public boolean add(E e) {
        modCount++;  //Represents the number of operations on ArrayList, about the fast-fail mechanism, and then
        //Parameter value: not A parameter is a URL, array object, 2
        add(e, elementData, size); //Call methods in classes
        return true; //Return results
    } // End Code
  • The above can be said to be very simple, but when debug came, I found that there were already things in the elementData array of storage elements, as shown below.

  • Then when I debug step over to the end code, the program jumps to such a code.

    public synchronized void addURL(URL url) {
        if (closed || url == null)
            return;
        synchronized (unopenedUrls) {
            if (! path.contains(url)) {
                unopenedUrls.addLast(url);
                path.add(url);
            }
        }
    }
    //Jump again
    public InternalError(String message) {
        super(message);
    }
    //Jump back
    static {
        System.loadLibrary("instrument");
    }
    //There are still a lot of it...
  • Some of the above codes don't really need to know what they mean, but what you can tell us is that elementData in ArrayList is not just storing the elements we need to store, but loading some files or other things with elementData array for the first add, but not for the second add, and after loading some paths for the first add. Or after the library, elementData cleans them up, assuming they're loaded, and then it's time to store our elements, record a small video, and download it for a look.
  • After another experiment, except that the add method of ArrayList loads something for the first time, when we re-export ArrayList again, the first use of the add method will no longer be home for anything, as shown in the following test code

    List<String> list = new ArrayList<>();
    list.add("X");
    List<String> list2 = new ArrayList<>();
    list2.add("X");
  • When I debug, I will see many getLoader`loders'methods or attributes. So this is the first time I use ArrayList's class loading mechanism to work. I asked questions in the community, so you can see that associative class loading mechanism is very grateful for replying to my 13821484135822 `Big Brother, QA page: About Array List, please come in and have a look.
  • Back to the add method, we found that it would call another add method in the class. The source code is as follows

    private void add(E e, Object[] elementData, int s) {
      //Expansion if full
        if (s == elementData.length)
            elementData = grow();
        //Then save the elements to the specified location of the array
        elementData[s] = e;
        //add one-tenth
        size = s + 1;
    }
  • The source code of the group method used in this method is as follows

    private Object[] grow() {
        return grow(size + 1);
    }
    private Object[] grow(int minCapacity) {
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }
    //Returns at least the same capacity as the given minimum capacity
    private int newCapacity(int minCapacity) {
        int oldCapacity = elementData.length;
        //10 + (10 > 1) = 15, that is, 1.5 times of capacity expansion
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //If the expanded capacity is smaller or equal to the smallest capacity
        if (newCapacity - minCapacity <= 0) {
          //Determine if it's initialization?
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            //If yes, return to the default capacity 10 directly, and you can see that the size has just been initialized.
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) // overflow error, minimum cannot be negative
                throw new OutOfMemoryError();
            //If it's not initialization or parameter error, it returns the smallest capacity.
            return minCapacity;
        }
        //This means that the expanded capacity is larger than the minimum capacity.
        //Whether the maximum length has been reached, if not, return to the expanded length, otherwise call hugeCapacity
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow error
            throw new OutOfMemoryError();
        //If the specified maximum array length is reached, the maximum length of the integer is expanded, otherwise the current default maximum array length is reached.
        return (minCapacity > MAX_ARRAY_SIZE)
            ? Integer.MAX_VALUE
            : MAX_ARRAY_SIZE;
    }
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
  • There's also an add method at a specified location. Here's the source implementation

    public void add(int index, E element) {
        //Check if the array crosses the bounds
        rangeCheckForAdd(index);
        modCount++;
        final int s; //Temporarily save size
        Object[] elementData; //Temporarily save elementData
        if ((s = size) == (elementData = this.elementData).length)
            elementData = grow(); //If the length equals size, it means expansion.
        //The core method, System.arraycopy, will leave out the index location where you want to operate.
        System.arraycopy(elementData, index,
                         elementData, index + 1,
                         s - index);
        //Then the index empties out the position assignment               
        elementData[index] = element;
        //add one-tenth
        size = s + 1;
    }
  • Well, the add method of ArrayList has been introduced. If there is something wrong, please correct it.

addAll

  • Source code implementation

    public boolean addAll(Collection<? extends E> c) {
      //Transform array
        Object[] a = c.toArray();
        modCount++;
        //Gets the length of the incoming collection
        int numNew = a.length;
        //If it is an empty set, return directly
        if (numNew == 0)
            return false;
        Object[] elementData;
        final int s;
        //If the length of the incoming collection is greater than the number of empty locations in elementData, it grows
        if (numNew > (elementData = this.elementData).length - (s = size))
            elementData = grow(s + numNew);
        //copy the incoming data as soon as the growth is complete
        System.arraycopy(a, 0, elementData, s, numNew);
        //Element Number Modification
        size = s + numNew;
        //Return to success
        return true;
    }

Get

  • Source code implementation

    public E get(int index) {
        //Check index
        Objects.checkIndex(index, size);
        //Then call the method
        return elementData(index);
    }
    E elementData(int index) {
      //Direct Subscription Elements
        return (E) elementData[index];
    }
  • It's easy to say no.

remove

  • Source code implementation by index deletion

    public E remove(int index) {
        //Check index
        Objects.checkIndex(index, size);
        final Object[] es = elementData;
        //Used for returning values
        E oldValue = (E) es[index];
    
        fastRemove(es, index);
        return oldValue;
    }
    private void fastRemove(Object[] es, int i) {
        modCount++;
        final int newSize;
        //If the length after removing an element is greater than the location of the specified deleted index
        if ((newSize = size - 1) > i)
        //Move the element behind the deleted element forward one bit
            System.arraycopy(es, i + 1, es, i, newSize - i);
        //Avoid memory leaks
        es[size = newSize] = null;
    }
  • Source code implementation by object deletion

    public boolean remove(Object o) {
        final Object[] es = elementData;
        final int size = this.size;
        int i = 0;  //First occurrence of element location record
        //The Logic of Finding Elements
        found: {
            if (o == null) {
                for (; i < size; i++)
                    if (es[i] == null)
                        break found;
            } else {
                for (; i < size; i++)
                    if (o.equals(es[i]))
                        break found;
            }
            //No one found it at this point. Return to false.
            return false;
        }
        //If found, delete it according to subscript
        fastRemove(e  s, i);
        return true;
    }

indexOf

  • Source code implementation

    public int indexOf(Object o) {
        return indexOfRange(o, 0, size);
    }
    int indexOfRange(Object o, int start, int end) {
        Object[] es = elementData;
        if (o == null) {
            for (int i = start; i < end; i++) {
                if (es[i] == null) {
                  //Return the index when you find the first element you want to wait for
                    return i;
                }
            }
        } else {
            for (int i = start; i < end; i++) {
                if (o.equals(es[i])) {
                  //Return the index when you find the first element you want to wait for
                    return i;
                }
            }
        }
        //No Return-1 was found
        return -1;
    }
  • This implementation is relatively simple, and some of the * indexOf * similar methods of ArrayList have the same general idea.

writeObject

  • This is the method of serialization.

    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        //Record the number of operations
        int expectedModCount = modCount;
        //Write the no-static and no-transient s attributes in the class into the stream
        s.defaultWriteObject();
        s.writeInt(size);
        //Write out the elements in turn
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }
        //If the contents of the array are changed again, serialization stops and an exception is thrown
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

readObject

  • This is the way to deserialize

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read size and other content
        s.defaultReadObject();
        //Read capacity
        s.readInt();
        //If read size > 0
        if (size > 0) {
            // Like clone(), arrays are allocated based on size rather than capacity
            SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, size);
            //Create storage arrays
            Object[] elements = new Object[size];
            //Read the data out and assign values in turn
            for (int i = 0; i < size; i++) {
                elements[i] = s.readObject();
            }
            //Then assign the global variables to this class
            elementData = elements;
        } else if (size == 0) {
          //If zero represents an empty set, use constant substitution directly
            elementData = EMPTY_ELEMENTDATA;
        } else {
          //Illegal, abnormal
            throw new java.io.InvalidObjectException("Invalid size: " + size);
        }
    }
  • Okay, so I'm going to finish the introduction of ArrayList. So I have only one question about how elementData loads jar s or paths on the first add. If you know, please comment on the following. Thank you very much.
  • Summarize ArrayList. When we analyze the source code, we design synchronous loading methods for other classes except adding paths for the first time. But this method does not involve synchronization, so ArrayList is not thread-safe, and it is lazy to load. The first default initialization will not be initialized as initialization capacity, but will be initialized later. Initialization capacity, the core of this class is the System.arraycopy method, so we can use this native method to make the program faster and more accurate when we move the operation array in the future. It is quite rapid and direct when adding and get ting, that is, to move the location element back and insert the element at this location, so the increase and query of ArrayList is very fast. But we also need to note that when ArrayList's elements are full, it creates new arrays for copying, so when ArrayList's elements are quite large, it's a terrible thing, so ArrayList is not suitable for storing many elements.

LinkedList

  • You can refer to this article about linked lists first, if you have a little knowledge, you can not read it: linked list
  • This is an implementation class of List implemented by linked list. For ArrayList, there is no problem of expanding copy. If you know a little bit about the content of linked list, you will know that adding elements in the linked list is nothing more than changing the reference. So it has no such problem, so let's go directly to the source code.
  • Still look at his membership attributes first.

      //Element number
      transient int size = 0;
      //Header node
      transient Node<E> first;
      //Tail node
      transient Node<E> last;
  • transient stands for not being serialized, so what is Node? Look at the source code.

    private static class Node<E> {
        E item;  //Value of this element
        Node<E> next; //Pre-element pointing
        Node<E> prev; //Post-element pointing
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
  • From this, we can see that LinkedList implementation is a double linked list. Let's look at the implementation of its initialization method.

    public LinkedList() {
    }
  • Nothing has been done to construct the empty parameter. Let's look at other initialization methods.

    public LinkedList(Collection<? extends E> c) {
        this();
        addAll(c);
    }
  • So the main logic of this parametric construction method is actually the addAll method. I will not talk about this method for the time being, so let's first look at the other core implementation methods.

add

  • Source code is as follows

    public boolean add(E e) {
        linkLast(e);  //Add to the end of the list
        return true;
    }
    void linkLast(E e) {
      //Retain the last element reference
        final Node<E> l = last;
        //Create a new element with reference to the previous element of the new object based on the add ed parameter passed in
        //That's the last element before.
        final Node<E> newNode = new Node<>(l, e, null);
        //Point the last element pointer to the new element
        last = newNode;
        //If the previous tail element is empty, it represents an empty list.
        if (l == null)
        //That is, it's the element at the beginning and at the end.
            first = newNode;
        else
        //Instead of an empty list, the next element of the penultimate element is the new element.
            l.next = newNode;
        size++;
        modCount++;
    }
  • Another is insertion based on index location

    public void add(int index, E element) {
      //Is it across the border?
        checkPositionIndex(index);
        //If index equals the number of elements
        if (index == size)
        //Then add it to the tail
            linkLast(element);
        else
        //Otherwise add by location
        //The node method, passing in the index, returns the (non-null) node at the index of the specified element
            linkBefore(element, node(index));
    }
    void linkBefore(E e, Node<E> succ) {
        //It has been ensured that succ is not empty, which is ensured in the node method above.
        //Remove the previous element reference of the element on the specified index
        final Node<E> pred = succ.prev;
        //Create a new element, the previous element of the new element is the previous element that currently specifies the element on index
        //The next element is the one on index
        final Node<E> newNode = new Node<>(pred, e, succ);
        //Point the front pointer of the original element at the specified index position to the new element
        succ.prev = newNode;
        //If added in the header, the previous element of the current element must be empty
        if (pred == null)
        //Then the new element becomes the head element.
            first = newNode;
        else
        //Otherwise, point the back pointer of the element at index-1 to the new element
            pred.next = newNode;
        size++;
        modCount++;
    }
  • If you are familiar with linked lists, then the above code is very simple to understand, if you do not understand, you can draw your own picture, instantly understand.

addAll

  • This is also the method invoked in the construction. Let's take a look at the implementation.

    public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }
    public boolean addAll(int index, Collection<? extends E> c) {
        //Check if the location is beyond the boundaries
        checkPositionIndex(index);
        //Set Rotation Array
        Object[] a = c.toArray();
        //Determine the number of elements in a set
        int numNew = a.length;
        //If it is an empty collection, return
        if (numNew == 0)
            return false;
        //Pointing before recording and current node
        Node<E> pred, succ;
        //If equal representation is added at the end
        if (index == size) {
          //In the final addition, you don't need the current element, because last has pointed to
            succ = null;
            //When adding a collection, the previous node of the first added element in the collection is the last node in the current list.
            pred = last;
        } else {
            //This means that instead of appending at the end, elements are appended to the middle of the list.
            //The node method has said before that it returns the node at the index location based on the index
            //Save the reference when the node returns to the node
            succ = node(index);
            //Record the reference of the front node of the current index node
            pred = succ.prev;
            //At this point, the references of the current node and the previous node are recorded.
        }
        //Start looping to create the node of the element in the collection, and then modify the relevant pointer
        for (Object o : a) {
            //Strengthen the set elements, generic constraints do not classcast
            E e = (E) o;
            //Create a node with the previous element pointing to the determined node
            Node<E> newNode = new Node<>(pred, e, null);
            //Representatives added from scratch
            if (pred == null)
            //The new node is the first node.
                first = newNode;
            else
            //The new node points to pred and perd. next points to the new element, so it forms a two-way link between the former element and the new element.
                pred.next = newNode;
            //At this point, the old node and the new node are connected. We need to move pred to point to the new node.
            //Then the new node is treated as the old node, and then the newly created node is used as a two-way link.
            pred = newNode;
        }
        //Now that all the elements of the linked list are connected from the beginning to the set, we need to deal with the link problem of the tail node of the set and the original index node of the linked list.
        //If the original tail index node does not exist
        if (succ == null) {
          //Then last points to the tail node of the collection
            last = pred;
        } else {
          //If there is an index node representing the previous list, then modify the link between the index node and the tail node of the collection
            pred.next = succ;
            succ.prev = pred;
        }
        //Do some counting
        size += numNew;
        modCount++;
        return true;
    }
  • In fact, the add method is basically parsed, so by the way, there is no problem with the previous structure. Here is the source code of other methods.

remove

  • remove()

    public E remove() {
        return removeFirst();
    }
    //You can see that the default deletion starts from scratch
    public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
    }
    private E unlinkFirst(Node<E> f) {
        // assert f == first && f != null;
        //Remove header element attribute values
        final E element = f.item;
        //Remove the reference to the next element of the header element
        final Node<E> next = f.next;
        //Empty the attribute value of the header element
        f.item = null;
        f.next = null; // help GC
        //Then point the first pointer to the next element
        first = next;
        //If only one element exists
        if (next == null)
        //first and last are null
            last = null;
        else
        //Otherwise, the previous reference of the next node of the original header node will be cancelled, because it does not exist, and it has become the header node itself.
            next.prev = null;
        size--;
        modCount++;
        return element;
    }
  • remove(Object)

    public boolean remove(Object o) {
      //Traversal, the logic is relatively simple, not a word of code, a word of code.
        if (o == 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;
    }  
    //The overall logic is that once a node has been acquired, the reference relationship between the front and back of the node is found, and then the reference relationship is changed.
    E unlink(Node<E> x) {
        // assert x != null;
        //Remove all attribute values of an element
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;
        //If the preceding reference is null, it represents the header element
        if (prev == null) {
          //Header pointer, pointing to the next element
            first = next;
        } else {
            //Then the previous reference is not empty
            //Point the back pointer of the front element and the back pointer of the element to the latter element of the element.
            prev.next = next;
            //The preceding reference to this node can be cancelled
            x.prev = null;
        }
        //If the postposition reference is empty
        if (next == null) {
          //Represents the tail node, the tail pointer, pointing to the previous one
            last = prev;
        } else {
          //Represents a non-tail node that points the preceding reference of the latter element of the secondary element to the former element of the secondary element.
            next.prev = prev;
            //Posterior references to minor elements can be cancelled
            x.next = null;
        }
        //To this x node, it is completely out of the list and empty
        x.item = null;
        size--;
        modCount++;
        return element;
    }
  • removeFirst()

    public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f); //The method implementation has been written above.
    }
  • removeLast()

    public E removeLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return unlinkLast(l); // Similar to the previous unlinkFirst, I won't go into details.
    }
  • remove(int)

    public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index)); //The implementation is explained in the front
    }
  • removeFirstOccurrence&removeLastOccurrence

    • Its source code is not implemented, the function of this method is: if the specified deletion element is deleted in the list, (difference: delete the first appeared & delete the last appeared), if there is no list unchanged.

Get

  • GetFirst () & getLast (), which is easy to implement without commenting

    public E getFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return f.item;
    }
    public E getLast() {
        final Node<E> l = last;
        if (l == null)
            throw new NoSuchElementException();
        return l.item;
    }
  • get(int), easy to implement without commenting

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

set

  • set(int,E)

    public E set(int index, E element) {
      //Check index
        checkElementIndex(index);
        //Using node method to extract nodes on index
        Node<E> x = node(index);
        //Save as return
        E oldVal = x.item;
        //replace
        x.item = element;
        return oldVal;
    }

Queue operation

  • LinkedList also supports queue operations, and its implementation is relatively simple. It relies on the previously introduced add and remove methods (unlink), so it is not intended to post the source code. The supported operations are similar to peek,poll,push,pop and so on. Only the enumeration part is listed.

writeObject&readObject

  • As for the serialization mechanism of LinkedList, it is similar to ArrayList's serialization method and steps. First, the no-static and no-transient s attributes in the class are written into the stream, then the size is written, then the elements are written in turn, and deserialization is the same step.
  • Summary: We can see that LinkedList is a bi-directional linked list implementation, and does not have a head-to-tail connection, so it is not a circular linked list, and there is no concept of initialization capacity, and there is no capacity constants in ArrayList, so this class can be theoretically infinite, and there is no synchronization code block found, so this class is not synchronized, we need to use it. When using scenarios, for other operations, it is a regular list operation.

Posted by gemmerz on Sun, 27 Jan 2019 20:12:14 -0800