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