[Collection series] - analyze the List interface in Collection in a simple way

Keywords: Java Programming

I. Introduction to List

The data structure of List is a sequence. When storing content, a continuous space is directly opened up in memory, and then the spatial address is corresponding to the index.

The following is a simple schema of List collection

From the inheritance relationship in the figure, we can know that ArrayList, LinkedList, Vector and Stack are all four implementation classes of List.

  • AbstractCollection is an abstract class, which is the only class that implements the Collection interface. AbstractCollection mainly implements methods such as toArray(), toArray(T[] a), and remove().
  • AbstractList is also an abstract class, which inherits from AbstractCollection. AbstractList implements functions other than size(), get(int location) in the List interface, such as the specific iterator ListIterator.
  • AbstractSequentialList is an abstract class that inherits from AbstractList. AbstractSequentialList implements "all functions of the linked list that operate according to the index value in the linked list".
  • ArrayList is a dynamic array, which is implemented by arrays. Random access efficiency is high, random insertion and random deletion efficiency is low.
  • LinkedList is a two-way linked list. It can also be operated as a stack, queue, or two-end queue. LinkedList has low random access efficiency, but high random insertion and deletion efficiency.
  • Vector is also a dynamic array. Like ArrayList, it is also implemented by array. But ArrayList is not thread safe, and vector is thread safe.
  • Stack is a stack, which inherits from Vector. Its features are: Filo, first in last out.

Next to each implementation class for method analysis!

II. ArrayList

ArrayList implements the List interface, which is also a sequence container. That is to say, the data stored by the elements is the same as the order put in, and null elements are allowed to be put in. The underlying layer is implemented by arrays.
Except that this class does not implement synchronization, it is roughly the same as Vector.

After Java 1.5, the collection also provides generics. Generics are just the syntax sugar provided by the compiler, which is convenient for programming and has no substantial impact on the program. Because all classes inherit to Object by default, the array here is an Object array, so that it can hold any type of Object.

Introduction to common methods

2.1. get method

The get() method is also very simple. First, determine whether the incoming subscript is out of bounds, and then get the specified element.

public E get(int index) {
        rangeCheck(index);
        return elementData(index);
}

/**
 * Check whether the incoming index is out of range
 */
private void rangeCheck(int index) {
        if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

2.2 set method

The set() method is also very simple. You can directly assign a value to the specified position of the array.

public E set(int index, E element) {
        rangeCheck(index);
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
}

2.3 add method

There are two ways to add elements to ArrayList: add (e e e) and add (int index, e e e).
Both of these methods are to add new elements to the container, which may result in insufficient capacity. Therefore, before adding elements, you need to check the remaining space, and automatically expand the capacity if necessary. The expansion operation is finally completed through the grow() method.

grow method implementation

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);//1.5 times the original
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
}

There is another addAll() method for adding elements. The addAll() method can add multiple elements at a time. There are two methods depending on the location. One is the addall (collection <? Extends E > C) method added at the end, and the other is the addall (int index, collection <? Extends E > C) method inserted from the specified location.

Difference: the time complexity of addAll() is not only related to the number of inserted elements, but also to the location of the insertion. The time complexity is linear growth!

2.4 remove method

There are two versions of the remove() method, one is remove(int index) to delete the element at the specified location; the other is remove(Object o) to delete the first satisfied element through o.equals(elementData[index]).

You need to move the element after the delete point one position forward. Note that for GC to work, you must explicitly assign a null value to the last location.

  • remove(int index) method
public E remove(int index) {
        rangeCheck(index);
        modCount++;
        E oldValue = elementData(index);
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; //Assign null value for GC recovery
        return oldValue;
}
  • remove(Object o) method
public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
}

III. LinkedList

In the previous article, we know that LinkedList implements both the List interface and the Deque interface, that is, it can be regarded as a sequence container, a Queue and a Stack.

The bottom layer of LinkedList is realized by two-way linked list. It points to the first and last elements of linked list respectively through first and last references. Note that there is no dummy element (if a parameter is not used in subprogram or function, it is called dummy element). When the linked list is empty, first and last point to null.

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
     /**capacity*/
    transient int size = 0;

    /**First element of the list*/
    transient Node<E> first;

     /**Last element of the list*/
    transient Node<E> last;
    
    ......
}
/**
 * Internal class Node
 */
private static class Node<E> {
    E item;//element
    Node<E> next;//Successor
    Node<E> prev;//precursor
    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

Introduction to common methods

3.1. get method

The get() method is also very simple. First, determine whether the incoming subscript is out of bounds, and then get the specified element.

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

/**
 * Check whether the incoming index is out of range
 */
private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

3.2 set method

The set(int index, E element) method modifies the element at the specified subscript to the specified value. It also finds the reference corresponding to the following table element through node(int index), and then modifies the value of item in Node.

public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
}

3.3 add method

Similarly, the add() method has two methods, one is add (e e e), the other is add(int index, E element).

  • Add (e e e) method

This method inserts elements at the end of LinkedList, because last points to the end of the list, and the cost of inserting elements at the end is a constant time, only a few related references need to be modified.

public boolean add(E e) {
        linkLast(e);
        return true;
}

/**
 * Additive elements
 */
void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            //The original list is empty. This is the first element inserted
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
}
  • add(int index, E element) method

This method is to insert elements in the specified table. First, find the specific location through linear search, and then modify the relevant references to complete the insertion operation.

It is divided into two steps: 1. Find the location to insert according to the index; 2. Modify the reference to complete the insertion.

public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            //Call the add method to add elements directly at the end
            linkLast(element);
        else
            //Find the location to insert according to index
            linkBefore(element, node(index));
}

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

Similarly, there is another addAll() method for adding elements. The addAll() method can add multiple elements at a time. There are two methods depending on the location. One is the addall (collection <? Extensions E > C) method added at the end, and the other is the addall (int index, collection <? Extensions E > C) method inserted from the specified location.

The time complexity of addAll() is not only related to the number of inserted elements, but also related to the location of the insertion. The time complexity is linear growth!

3.4 remove method

Similarly, the remove() method has two methods, one is to delete the element remove(int index) at the specified subscript, the other is to delete the first element remove(Object o) equal to the specified element.

Two deletion operations are: 1. Find the reference of the element to be deleted; 2. Modify the related reference to complete the deletion.

  • remove(int index) method

From the following table, find the corresponding node and delete it

public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
}
  • remove(Object o) method

Find the corresponding node through equals judgment, and then delete it

public boolean remove(Object o) {
        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 deletion is done by unlink (node < E > x). Here we need to consider the boundary situation when the deletion element is the first or last.

/**
 * Delete a Node method
 */
E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;
        
        //First element removed
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }
        //Last element removed
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
}

Four, Vector

Vector class belongs to a saved subclass, which existed as early as jdk1.0. However, after jdk1.2, the concept of set is emphasized. Therefore, many new interfaces have been defined successively, such as ArrayList and LinkedList. However, considering that most of the early days are used to vector class, java designers have made vector more implemented for compatibility A List interface, which keeps it.

In terms of use, the get, set, add and remove methods of Vector are basically the same as ArrayList. The difference is that Vector adds thread synchronization lock synchronized to the methods, so the execution efficiency will be slower!

4.1. get method

public synchronized E get(int index) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        return elementData(index);
}

4.2 set method

public synchronized E set(int index, E element) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
}

4.3 add method

public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
}

4.4 remove method

public synchronized boolean removeElement(Object obj) {
        modCount++;
        int i = indexOf(obj);
        if (i >= 0) {
            removeElementAt(i);
            return true;
        }
        return false;
}

Five, Stack

In Java, the stack class represents the stack of LIFO objects. Stack is a very common data structure, which is completed by the typical operation mode of first in first out. In real life, the bullet of pistol cartridge is a typical structure of last in first out.

In terms of use, the main methods are push, peek and pop.

5.1 push method

push method to add elements to the stack

public E push(E item) {
        addElement(item);
        return item;
}

5.2 peek method

The peek method means to look at the object at the top of the stack, but not remove it from the stack

public synchronized E peek() {
        int     len = size();
        if (len == 0)
            throw new EmptyStackException();
        return elementAt(len - 1);
}

5.3. pop method

The pop method represents the element method to be removed

  public synchronized E pop() {
        E       obj;
        int     len = size();
        obj = peek();
        removeElementAt(len - 1);
        return obj;
}

There are many doubts about stack class in Java. Stack is more suitable to be implemented with queue structure, which makes stack not rigorous in design. Therefore, the official recommendation is to use the class under Deque to implement stack!

Six, summary

  • ArrayList (dynamic array structure), fast query (random access or sequential access), slow add and delete, but insert at the end, the speed is similar to LinkedList!
  • Linked list (two-way linked list structure), slow query, fast add and delete!
  • Vector (dynamic array structure), which is slower than ArrayList, is replaced by ArrayList and is basically not used. The advantage is thread safety (functions are synchronized). If you need to use it under multi-threaded conditions, it is recommended to use the tool class in the concurrent container for operation, which is highly efficient!
  • Stack (stack structure) inherits from Vector. The data is first in, then out. It is not used basically. If you want to implement the stack, it is recommended to use ArrayDeque under Deque, which is more efficient than stack!

Seven, reference

1. Jdk1.7 & JDK1.8 source code
2,CarpenterLee - Java set analysis
3,Comparison of blog Garden - deadwood - ArrayList, LinkedList, Vector and Stack

Author: fried chicken cola
Source: www.pzblog.cn

Posted by ducky on Sat, 16 Nov 2019 07:19:28 -0800