summary
A List is an ordered set (also known as a sequence). Users of this interface have precise control over where each element in the List is inserted. Users can access elements through their integer index (position in the List) and search for elements in the List. In fact, it is just a idiom to say that it is a sub interface of the Collection interface (Collection has many sub interfaces, which is one of the three main sub interfaces, and the other two will be mentioned later). Therefore, the methods defined in the Collection interface can also be used in the List interface. In addition, according to the characteristics of List, Other methods are introduced.
Features of List interface:
- Elements are stored in a linear manner
- Element access is orderly, that is, the storage order of elements is consistent with the extraction order.
- The element has an index. Through the index, you can accurately operate the elements in the collection (similar to arrays)
- The element can be repeated. The equals method of the element is used to compare whether it is a repeated element
Use of List
Common methods of List
Basic introduction
Common methods mentioned here refer to methods other than implementing the Collection interface. As mentioned earlier, the elements in the List set can be operated by index, so some methods to operate the elements of the set according to the index are added to the List set. These methods are briefly introduced below:
- void add(iint index, E element): inserts the specified element at the specified position in the list
- Boolean addall (int index, collection <? Extensions E > C): inserts all elements in the specified collection into the list at the specified location
- E get(int index): returns the element at the specified position in this list
- List subList(int fromIndex, int toIndex): returns the set of some objects in the list, that is, the returned set is a subset of the list and is the value of the subscript index. The parent set list starts with fromIndex (included) and ends with toIndex (not included) as the returned subset
- int indexOf(Object obj): returns the index of the first occurrence of the specified element in the list. If the list does not contain elements, returns - 1
- int lastIndexOf(Object obj): returns the index of the last occurrence of the specified element in the list. If the list does not contain elements, returns - 1
- E remove(int index): removes the element at the specified position in this list
- E set(int index, E element): replace the element at the specified position in this list with the specified element
Code example
public class ListDemo { public static void main(String[] args) { // Create a List collection object through the implementation class ArrayList of List List<String> list = new ArrayList<String>(); // Add element at specified location list.add(0,"jack"); list.add(1,"rose"); list.add(2,"marry"); System.out.println(list); // Delete element with index position 2 list.remove(2); System.out.println(list); // The specified element replaces the element at the specified position in this list list.set(0, "Lao Wang"); System.out.println(list); // Get the element at the specified position (also traverse the output) for(int i = 0;i<list.size();i++){ System.out.println(list.get(i)); } //You can also use enhanced for for (String string : list) { System.out.println(string); } } }
Implementation class of List
As an interface, the implementation class of List is used when we create objects (the ArrayList implementation class is used in the above code example). In the List interface, there are three common implementation classes: ArrayList, Vector and LinkedList. The following analyzes and introduces them from the source code.
ArrayList
The bottom layer of ArrayList is realized through array. ArrayList can be dynamically expanded with the increase of elements. It is an array queue, which is the most used class in the Java collection framework, but it is thread unsafe.
- Features: it is stored in the form of array, so the random access speed is fast, so it is suitable for query
- Disadvantages: not applicable to insert and delete operations, because each operation needs to move the elements in the array; Thread unsafe
Let's take a look at the source code of ArrayList
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{ /** * Default initial capacity. If no length is specified during initialization, the default length of 10 is used */ private static final int DEFAULT_CAPACITY = 10; /** * Shared empty array instance used for empty instances. Empty array */ private static final Object[] EMPTY_ELEMENTDATA = {}; /** * Shared empty array instance used for default sized empty instances. We * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when * first element is added. Empty array */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /** * Constructs an empty list with an initial capacity of ten.Construct an initial capacity of 10 */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//Initialize to empty array } public boolean add(E e) { //Check whether the current array has enough more than one element ensureCapacityInternal(size + 1); // Increments modCount!! //Save the new element to the [size] position, and then increase the size by 1 elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity(Object[] elementData, int minCapacity) { //If the current array is still empty if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //Then minCapacity takes default_ Maximum value of capacity and minCapacity return Math.max(DEFAULT_CAPACITY, minCapacity); } //Check whether capacity expansion is required return minCapacity; } private void ensureExplicitCapacity(int minCapacity) { modCount++;//Number of modifications plus 1 // If the required minimum capacity is larger than the length of the current array, that is, the current array is not enough, expand the capacity if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length;//Current array capacity int newCapacity = oldCapacity + (oldCapacity >> 1);//The capacity of the new array is 1.5 times that of the old array //See if 1.5 times of the old array is enough if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //See if the 1.5 times of the old array exceeds the maximum array limit if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); //Copy a new array elementData = Arrays.copyOf(elementData, newCapacity); } public boolean remove(Object o) { //First find o the subscript in the array of the current ArrayList //Discuss whether o is empty or not if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) {//null values are compared with = = fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) {//Non null values are compared with equals fastRemove(index); return true; } } return false; } private void fastRemove(int index) { modCount++;//Number of modifications plus 1 //Number of elements to be moved int numMoved = size - index - 1; //If you need to move elements, use system Arraycopy move element if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); //Empty the elementData[size-1] position to allow GC to reclaim space and reduce the number of elements elementData[--size] = null; // clear to let GC do its work } public E remove(int index) { rangeCheck(index);//Check whether the index is legal modCount++;//Number of modifications plus 1 //Take out the element at [index]. The element at [index] is the element to be deleted, which is used to return the deleted element finally E oldValue = elementData(index); //Number of elements to be moved int numMoved = size - index - 1; //If you need to move elements, use system Arraycopy move element if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); //Empty the elementData[size-1] position to allow GC to reclaim space and reduce the number of elements elementData[--size] = null; // clear to let GC do its work return oldValue; } public E set(int index, E element) { rangeCheck(index);//Check whether the index is legal //Take out the element at [index]. The element at [index] is the element to be replaced, which is used to return the replaced element finally E oldValue = elementData(index); //Replace the element at [index] with element elementData[index] = element; return oldValue; } public E get(int index) { rangeCheck(index);//Whether the index test is legal return elementData(index);//Returns the element at the [index] position } public int indexOf(Object o) { //There are two cases: o is empty or not if (o == null) { //Look from front to back for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1; } public int lastIndexOf(Object o) { //There are two cases: o is empty or not if (o == null) { //Look back and forward for (int i = size-1; i >= 0; i--) if (elementData[i]==null) return i; } else { for (int i = size-1; i >= 0; i--) if (o.equals(elementData[i])) return i; } return -1; }
From the above source code, we can see:
- When initializing ArrayList, if we do not specify the length, it will have a default length of 10, which will be increased by 1.5 times each time
- Then is the source code introduction of some common methods of ArrayList
Vector
The bottom layer of Vector is also realized through array, and the method is basically the same as ArrayList,. But Vector is thread safe This is because it adds the synchronized keyword to ensure thread safety.
- Advantages: it is stored in the form of array, so the random access speed is fast, so it is suitable for query; Thread safety
- Disadvantages: not applicable to insert and delete operations, because each operation needs to move the elements in the array
Let's look at the source code of Vector
/** * Constructs an empty vector so that its internal data array * has size {@code 10} and its standard capacity increment is * zero. */ public Vector() { this(10); //Specify an initial capacity of 10 } public Vector(int initialCapacity) { this(initialCapacity, 0);//Specify a capacityIncrement increment of 0 } public Vector(int initialCapacity, int capacityIncrement Increment is 0) { super(); //The validity of the initial capacity of the formal parameter initialCapacity is judged if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); //Created an array of type Object [] this.elementData = new Object[initialCapacity];//The default is 10 //Increment, the default is 0. If it is 0, it will be increased by 2 times later. If it is not 0, it will be increased by the increment you specify later this.capacityIncrement = capacityIncrement; } //synchronized means thread safe public synchronized boolean add(E e) { modCount++; //See if capacity expansion is needed ensureCapacityHelper(elementCount + 1); //Store the new element in [elementCount]. After saving, the number of elementCount elements will increase by 1 elementData[elementCount++] = e; return true; } private void ensureCapacityHelper(int minCapacity) { // overflow-conscious code //See if the capacity of the current array is exceeded if (minCapacity - elementData.length > 0) grow(minCapacity);//Capacity expansion } private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length;//Gets the length of the current array //If the capacity increment is 0, the new capacity = 2 times the oldCapacity //If the capacityIncrement increment is 0, the new capacity = oldCapacity + capacityIncrement increment; int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); //If the new capacity calculated above is not enough, expand the minCapacity according to the minimum capacity you specify if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //If the new capacity exceeds the maximum array limit, it is processed separately if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); //Copy the data from the old array to the new array. The length of the new array is newCapacity elementData = Arrays.copyOf(elementData, newCapacity); } public boolean remove(Object o) { return removeElement(o); } public synchronized boolean removeElement(Object obj) { modCount++; //Find the subscript of obj in the current Vector int i = indexOf(obj); //If I > = 0, it indicates that it exists. Delete the element at [i] if (i >= 0) { removeElementAt(i); return true; } return false; } public int indexOf(Object o) { return indexOf(o, 0); } public synchronized int indexOf(Object o, int index) { if (o == null) {//The element to find is a null value for (int i = index ; i < elementCount ; i++) if (elementData[i]==null)//If it is a null value, use = = null to judge return i; } else {//The element to find is a non null value for (int i = index ; i < elementCount ; i++) if (o.equals(elementData[i]))//If it is a non null value, use equals to judge return i; } return -1; } public synchronized void removeElementAt(int index) { modCount++; //Judge the legitimacy of subscript if (index >= elementCount) { throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount); } else if (index < 0) { throw new ArrayIndexOutOfBoundsException(index); } //j is the number of elements to be moved int j = elementCount - index - 1; //If you need to move elements, call system Arraycopy to move if (j > 0) { //Move the index+1 position and the following elements forward //Move the element at the position of index+1 to the position of index, and so on //Move j in total System.arraycopy(elementData, index + 1, elementData, index, j); } //The total number of elements decreased elementCount--; //Empty the location of elementData[elementCount] to add new elements. The elements in the location are waiting to be recycled by GC elementData[elementCount] = null; /* to let gc do its work */ }
From the above source code, we can see:
- If we do not specify the length of Vector during initialization, it will have a default length of 10, which will be increased by 2 times each time
- Then there is the source code introduction of some common methods of Vector
LinkedList
The underlying data storage structure of LinkedList is linked list structure, and it is also a two-way linked list, which can realize two-way operation. In addition, LinkedList also implements the operation methods of stack and queue, so it can also be used as stack, queue and double ended queue, such as peek, push, pop and other methods.
- Advantages: it is stored in the form of linked list, so the random access speed is slow to query and fast to add and delete.
- Disadvantages: thread unsafe
Let's take a look at the source code
int size = 0; Node<E> first;//Record the position of the first node Node<E> last;//Record the position of the last node private static class Node<E> { E item;//Element data Node<E> next;//Next node Node<E> prev;//Previous node Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } } public boolean add(E e) { linkLast(e);//By default, the new element is linked to the end of the linked list return true; } void linkLast(E e) { final Node<E> l = last;//Record the original last node with l //Create a new node final Node<E> newNode = new Node<>(l, e, null); //Now the new node is the last node last = newNode; //If l==null, the original linked list is empty if (l == null) //Then the new node is also the first node first = newNode; else //Otherwise, link the new node to the next of the original last node l.next = newNode; //Increase in the number of elements size++; //Increase in modification times modCount++; } public boolean remove(Object o) { //There are two situations: whether o is empty or not if (o == null) { //Find the node x corresponding to o for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) { unlink(x);//Delete x node return true; } } } else { //Find the node x corresponding to o for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) { unlink(x);//Delete x node return true; } } } return false; } E unlink(Node<E> x) {//x is the node to be deleted // assert x != null; final E element = x.item;//Data of deleted node final Node<E> next = x.next;//The next node of the deleted node final Node<E> prev = x.prev;//The previous node of the deleted node //If the previous node is not deleted, it indicates that the first node is deleted if (prev == null) { //Then the next node of the deleted node becomes the first node first = next; } else {//The deleted node is not the first node //The next of the previous node of the deleted node points to the next node of the deleted node prev.next = next; //Disconnect the link between the deleted node and the previous node x.prev = null;//Make GC recycle } //If there is no node after the deleted node, it means that the deleted node is the last node if (next == null) { //Then the previous node of the deleted node becomes the last node last = prev; } else {//The deleted node is not the last node //The prev of the next node of the deleted node executes the previous node of the deleted node next.prev = prev; //Disconnect the deleted node from the next node x.next = null;//Make GC recycle } //The data of the deleted node is also set to be empty to make GC recycle x.item = null; //Decrease in the number of elements size--; //Increase in modification times modCount++; //Returns the data of the deleted node return element; }
From the above source code, we can see:
- LinkedList is based on linked list, so there is no expansion method. By default, the element is automatically expanded at the end
- Then is the source code introduction of some common methods of LinkedList
The difference between ArrayList and Vector
Their underlying structures are arrays, which we call dynamic arrays.
- ArrayList is a new version of dynamic array, which is thread unsafe and efficient. Vector is an old version of dynamic array, which is thread safe and inefficient.
- The capacity expansion mechanism of dynamic array is different. The capacity expansion of ArrayList is 1.5 times that of the original, and the capacity expansion of Vector is 2 times that of the original.
- The initialization capacity of the array. If the initialization capacity is not explicitly specified when building the collection object of ArrayList and Vector, the initial capacity of the internal array of Vector is 10 by default, while ArrayList is in jdk1 6 and earlier versions are also 10, while jdk1 In versions after 7, ArrayList is initialized to an empty array with a length of 0, and then an array with a length of 10 is created when the first element is added.