Classification of sets
First, let's look at the overall classification of sets.
Both Collection and Set Chinese can be translated into collections. However, from a Java programming perspective, Collections should be translated into containers and Sets into collections.
Both Collection and Set Chinese can be translated into collections. However, from a Java programming perspective, Collections should be translated into containers and Sets into collections.
List | Set | |
---|---|---|
Sequential | order | disorder |
Repeatability | Repeatable | Not Repeatable |
Indexes | By manipulating elements through an index, you can traverse them using a normal for loop | Without an index, you cannot use a normal for loop to traverse. |
1.ArrayList
Basic principles, advantages and disadvantages
The length of the array is fixed, for example, 100. When the number of elements exceeds 100, it expands and copies the previous array into a new one.
The drawbacks are:
- Array expansion + element copy will be slower, so don't insert data frequently.
- If you add an element to the middle of an array, all the elements behind the new element need to be moved one bit backwards, which is not good performance.
The advantages are:
- Because array-based implementations allow you to locate an element by its memory address, performance is high when randomly obtaining an element in the array.
Scenarios applicable:
- The elements of the ArrayList are arranged in the order in which they are inserted.
- Suitable for scenes with low insertion frequency.
Source Code Analysis
Constructor
The default constructor makes the internal array a default empty array.
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
The default initialization array size is 10, but it is not assigned at initialization time. See more List Array expansion is assigned in the first add.
The default value is too small, so constructing an ArrayList typically gives you a size, such as 100 data, to avoid arrays being too small.
public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } }
Source code for add() method
Causes an array element to move.
Each time you add data to the ArrayList, you will determine that the elements of the current array are full.
If it is full, it expands the array and copies the elements from the old array to the new one.
public boolean add(E e) { ensureCapacityInternal(size + 1); elementData[size++] = e; return true; }
Source code for set() method
public E set(int index, E element) { // Check if it is out of bounds rangeCheck(index); // Get the original value E oldValue = elementData(index); // Set New Value elementData[index] = element; // Return the original value return oldValue; } private void rangeCheck(int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } transient Object[] elementData; @SuppressWarnings("unchecked") E elementData(int index) { // Get the original value of position i, Lisi return (E) elementData[index]; }
Source code for add(index, element) method
public void add(int index, E element) { // Judgement beyond bounds rangeCheckForAdd(index); // Make sure the array adds this element, directly + 1 ensureCapacityInternal(size + 1); // Make a copy of the data, see the notes System.arraycopy(elementData, index, elementData, index + 1, size - index); // The above is equivalent to moving the data one space backwards, and the current index is assigned elementData[index] = element; size++; } private void rangeCheckForAdd(int index) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); } public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); // Copy elementData from first to second, a total of two elements // System.arraycopy(elementData, 1, elementData, 2, 2);
Source code for get() method
The advantage is to take the elements out of the array directly.
public E get(int index) { // Check array bounds rangeCheck(index); // Return directly return elementData(index); }
Source code for remove() method
Causes an array element to move.
public class ArrayList<E> { public E remove(int index) { // Check for out-of-bounds rangeCheck(index); // Number of list length changes: add, delete modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) // Equivalent to moving all elements forward by one place at index+1 System.arraycopy(elementData, index+1, elementData, index, numMoved); // The last one is set to null to allow garbage collection to recycle elementData[--size] = null; // clear to let GC do its work return oldValue; } } public abstract class AbstractList<E> { protected transient int modCount = 0; }
Array expansion and element copy
By default, the first add() assigns 10 to the array by default.
Enter from the add() method.
Assuming that an array is 10 in size and has 10 elements added, the size of the array is 10 and the capacity is 10.
At this point, the add() method is called to insert an element, and the eleventh element cannot be inserted.
The default is to expand by 1.5 times.
transient Object[] elementData; public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static final int DEFAULT_CAPACITY = 10; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // elementData has been populated with 10 elements, minCapacity = 11 private static int calculateCapacity(Object[] elementData, int minCapacity) { // If it is an empty array if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // Compare to default 10 return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity(int minCapacity) { modCount++; // Indicates to expand the array // elementData.length defaults to 10 if (minCapacity - elementData.length > 0) // Expansion grow(minCapacity); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; // oldCapacity >> 1 = oldCapacity / 2 = 5 // newCapacity = 15 int newCapacity = oldCapacity + (oldCapacity >> 1); // Or too small if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // The new array is too large if (newCapacity - MAX_ARRAY_SIZE > 0) // Get a very large value newCapacity = hugeCapacity(minCapacity); // Finish copying the old array to the new one elementData = Arrays.copyOf(elementData, newCapacity); }
2.LinkedList
Basic principles, advantages and disadvantages
LinkedList, the bottom level is based on a two-way linked list data structure.
Advantage:
- A lot of insertions don't need to be scaled up like ArrayList, just keep hanging new nodes onto the list. Therefore, it is suitable for frequent insert and delete operations.
- LinkedList can be used as a queue, first in, first out, crashing in an element at the end of the list and pulling out an element from the beginning.
Disadvantages:
- It is not appropriate to get an element because you need to traverse the entire list until you find the element with index = 10.
Principle of inserting elements
add()
Insert an element at the end of the two-way list.
public boolean add(E e) { linkLast(e); return true; } /** * Links e as last element. */ void linkLast(E e) { // Original Tail Node final Node<E> l = last; // Encapsulating a new node specifically represents the following class final Node<E> newNode = new Node<>(l, e, null); // Cover last = newNode; // Indicates an empty List if (l == null) // Re-assign Head Node first = newNode; else // Change the next point of the original l l.next = newNode; size++; // list length is variable modCount++; } private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
add(index, element)
Inserts an element in the middle of the queue.
public void add(int index, E element) { // Guarantee >=0 <=size, just click in, it's easy checkPositionIndex(index); // Insert at the end of the queue if (index == size) linkLast(element); else linkBefore(element, node(index)); } // Get node at index location Node<E> node(int index) { // Insert position in the first half of the list if (index < (size >> 1)) { // Traverse from the beginning to get the node at the index position Node<E> x = first; for (int i = 0; i < index; i++) x = x.next; return x; } // Insert position in the second half of the queue else { // Traverse backwards from the tail to get the node at the index position Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } } void linkBefore(E e, Node<E> succ) { // assert succ != null; // Node at index position, the previous Node needs to point to a node with value e final Node<E> pred = succ.prev; // New Nodes final Node<E> newNode = new Node<>(pred, e, succ); // Point again, as above succ.prev = newNode; if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++; }
addFirst()
Insert an element at the head of the queue.
public void addFirst(E e) { linkFirst(e); } private void linkFirst(E e) { // Get Head Nodes final Node<E> f = first; // Create a new header node final Node<E> newNode = new Node<>(null, e, f); first = newNode; if (f == null) last = newNode; else // Original Head Node, Pointing to New Head Node f.prev = newNode; size++; modCount++; } private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
addLast()
Like the add() method, an element is inserted at the end.
public void addLast(E e) { linkLast(e); }
How to get elements
poll(), queued from the top of the queue
peek(), gets the element at the head of the queue, but the element at the head is not in the queue
getFirst()
getFirst() == peek()
Gets the element of the head, returns the first pointer directly to the data in the ode.
public E getFirst() { final Node<E> f = first; if (f == null) throw new NoSuchElementException(); return f.item; } transient Node<E> first;
getLast()
Gets the tail element.
public E getLast() { final Node<E> l = last; if (l == null) throw new NoSuchElementException(); return l.item; }
get(int index)
This method has poor performance and uses the node(index) method () to traverse the list of chains.
public E get(int index) { checkElementIndex(index); return node(index).item; } // Get node at index location Node<E> node(int index) { // Insert position in the first half of the list if (index < (size >> 1)) { // Traverse from the beginning to get the node at the index position Node<E> x = first; for (int i = 0; i < index; i++) x = x.next; return x; } // Insert position in the second half of the queue else { // Traverse backwards from the tail to get the node at the index position Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }
Principle of deleting elements
removeLast()
public E removeLast() { final Node<E> l = last; if (l == null) throw new NoSuchElementException(); return unlinkLast(l); } private E unlinkLast(Node<E> l) { // assert l == last && l != null; final E element = l.item; final Node<E> prev = l.prev; // Auto garbage collection for jvm l.item = null; l.prev = null; // help GC // Re-pointing the list of chains last = prev; // There was originally only one element if (prev == null) first = null; else prev.next = null; size--; modCount++; return element; }
removeFirst()
removeFirst() == poll() 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; final E element = f.item; final Node<E> next = f.next; // Re-pointing of Two-way Chain List f.item = null; f.next = null; // help GC first = next; if (next == null) last = null; else next.prev = null; size--; modCount++; return element; }
remove(int index)
public E remove(int index) { // Not out of bounds checkElementIndex(index); return unlink(node(index)); } 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; if (prev == null) { first = next; } else { // Pointing to Front Node prev.next = next; // To delete the forward and backward pointing of the node itself, also point to null, allowing garbage collection to go away x.prev = null; } if (next == null) { last = prev; } else { // Pointing to the back node next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; }
notes
You may have taken a note and looked for many articles to solve a problem. If there are sources that you have not posted, please inform us that the level is limited. If there are errors, you are welcome to comment.