Unless otherwise noted, the following source analysis is based on JDK 1.8.
A, the ArrayList
1. An overview
RandomAccess interface is implemented, so RandomAccess is supported. This makes sense, since ArrayList is implemented based on arrays.
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
Copy the code
The default size of the array is 10.
private static final int DEFAULT_CAPACITY = 10;
Copy the code
Expansion and 2.
EnsureCapacityInternal (); oldCapacity + (oldCapacity >> 1); That’s 1.5 times the old capacity.
Array.copyof () is called to copy the entire array into the new array, which is expensive, so it’s best to specify the approximate capacity of the ArrayList object when creating it to reduce the number of capacity expansion operations.
Public Boolean add(E E) {// Add elements using ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e;return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if(minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) {// overflow-conscious code int oldCapacity = elementdata.length; int newCapacity = oldCapacity + (oldCapacity >> 1);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: // minCapacity is usually close to size, so this is a win: // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }Copy the code
3. Delete elements
You need to call System.arrayCopy () to move all elements after index+1 one bit to the left. This operation has a time complexity of O(N), so you can see that ArrayList is very expensive to delete elements.
public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); Int numMoved = size-index -(index+1) -(index+1)+1 int numMoved = size-index -1;if(numMoved > 0) // Move all elements after index+1 one bit to the left, Arraycopy (elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear tolet GC do its work
return oldValue;
}
Copy the code
4. Fail-Fast
ModCount is used to count the number of times the ArrayList structure has changed. A structural change is any operation that adds or removes at least one element, or adjusts the size of an internal array. Simply setting the value of the element does not count as a structural change.
When serializing or iterative operations such as, need to compare the difference between before and after the operation modCount whether to change, if change the need to throw ConcurrentModificationException.
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, And any hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // Write out size as capacityfor behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for(int i=0; i<size; i++) { s.writeObject(elementData[i]); ModCount = modCount = modCount = modCount = modCountif (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
Copy the code
5. The serialization
ArrayList is implemented based on arrays and is dynamically scalable, so not all arrays holding elements will be used and there is no need to serialize them all.
The array elementData that holds elements uses the transient modifier, which states that the array will not be serialized by default.
transient Object[] elementData; // non-private to simplify nested class access
Copy the code
ArrayList implements writeObject() and readObject() to serialize only those parts of the array that have elements to fill them.
Serialization requires the writeObject() of ObjectOutputStream to convert the object into a byte stream and output it.
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for(int i=0; i<size; I++) {// use writeObject() of ObjectOutputStream to convert the object to a byte stream and output it. s.writeObject(elementData[i]); }if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
Copy the code
Deserialization uses ObjectInputStream’s readObject() method, which works similarly.
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity; // Allocate memory based upon size. EnsureCapacityInternal (size); Object[] a = elementData; // Readin all elements in the proper order.
for(int i=0; i<size; I++) {// use ObjectInputStreamreadObject() method to deserialize a[I] = s.readObject(); }}}Copy the code
System.arraycopy() and array.copyof () methods
The source code for array.copyof () internally calls the System.arrayCopy () method.
- The system.arrayCopy () method takes the target array and copies the original array to the target array, and can choose the starting point and length of the copy as well as the location in the new array.
- Arrays.copyof () automatically creates an array internally and returns the newly created array.
Secondly, the Vector
1. The synchronous
Its implementation is similar to ArrayList, but synchronized is used.
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
Copy the code
2. Comparison with ArrayList
- Vector is synchronous and therefore more expensive and slower than ArrayList. It is better to use an ArrayList rather than a Vector, because synchronization can be completely controlled by the programmer;
- Vector is twice as large as the old at a time, whereas ArrayList is 1.5 times larger.
3. Alternatives
You can use the Collections. SynchronizedList (); Get a thread-safe ArrayList.
List<String> list = new ArrayList<>();
List<String> synList = Collections.synchronizedList(list);
Copy the code
You can also use java.util.Concurrent and send the CopyOnWriteArrayList class under the package.
List<String> list = new CopyOnWriteArrayList<>();
Copy the code
Third, LinkedList
1. An overview
Based on bidirectional linked list, Node is used to store linked list Node information.
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
}
Copy the code
Each linked list stores first and last Pointers:
transient Node<E> first;
transient Node<E> last;
Copy the code
2. Add elements
Adds the element to the end of the list
public boolean add(E e) { linkLast(e); // This is the only method called herereturn true;
}
Copy the code
/** * e as the last element. */ void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; // If it is an empty bidirectional list, this node is both the last node and the head nodeif (l == null)
first = newNode;
elsel.next = newNode; // point to the next element, that is, to the next element, size++; modCount++; }Copy the code
Adds the element to the head of the list
public void addFirst(E e) {
linkFirst(e);
}
Copy the code
Private void linkFirst(e e) {final Node< e > f = first; final Node<E> newNode = new Node<>(null, e, f); // create a newNode with the first node as its successor. // If the list is empty, the last node also points to that nodeif(f == null) last = newNode; // Otherwise, the precursor pointer to the head node points to the new node, that is, to the previous elementelse
f.prev = newNode;
size++;
modCount++;
}
Copy the code
3. Delete the specified element
Public Boolean remove(Object o) {// If the deleted Object is nullif(o == null) {// Iterate from the beginningfor(Node<E> x = first; x ! = null; X = x.ext) {// Find the elementif(x.tem == null) {// Remove the found element from the list unlink(x);return true; }}}else{// Iterate from the beginningfor(Node<E> x = first; x ! = null; X = x.ext) {// Find the elementif(O.quals (x.tem)) {// Remove the found element from the list unlink(x);return true; }}}return false;
}
Copy the code
Unlink (Node<E> x) {// Assert! = null; final E element = x.item; final Node<E> next = x.next; Final Node<E> prev = x.rev; /** * Disconnect from prev */ / If the node to be deleted is a header node, delete the header node directlyif (prev == null) {
first = next;
} else{ prev.next = next; // Point the successor node of the precursor node to the successor node. /** * Disconnect from next */ / if the node to be deleted is a tail node, delete it directlyif (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
Copy the code
4. Comparison with ArrayList
- ArrayList is based on dynamic array and LinkedList is based on bidirectional LinkedList.
- ArrayList supports random access, LinkedList does not;
- LinkedList is faster to add and remove elements anywhere.