Outline
ArrayList is an implementation of the List interface, Java is the most commonly used container to achieve one class, it can be understood "variable array" is.
We know that when you need to specify the length of the Java array initialization, but can not change the designation. Internal ArrayList is an array, it features an array of enhancements made: The main expansion is a dynamic element in the increase in the container, which is the core of the ArrayList.
Front " JDK source code analysis -List, Iterator, ListIterator " has outlined a method of the List interface, the main method of List ArrayList basically the same, so this analysis focuses on the principles of its internal structure and expansion.
ArrayList inheritance structure below (not part of the interface):
Constructor
We start with the constructor to proceed with the analysis. ArrayList three constructors, respectively:
1. No argument constructor
/** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
The constructor involves two variables: elementData and DEFAULTCAPACITY_EMPTY_ELEMENTDATA. These two variables are defined as follows:
transient Object[] elementData; // non-private to simplify nested class access
Object elementData can see a type of an array, where the array is ArrayList as containers for storing data.
/** * 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. */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
DEFAULTCAPACITY_EMPTY_ELEMENTDATA is an empty array of type Object. Thus, the no-argument constructor role is to elementData initialized to an empty array of type Object.
2. Specify the initial capacity constructor
This configuration passed a parameter, i.e. initialCapacity initializes the internal capacity of the array, as follows:
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); } }
This configuration according to the incoming initial capacity (initialCapacity) for initializing the array variable storage elementData elements. When the initial capacity is 0, elementData is initialized to EMPTY_ELEMENTDATA, the following variables:
private static final Object[] EMPTY_ELEMENTDATA = {};
The array of DEFAULTCAPACITY_EMPTY_ELEMENTDATA is an empty Object array, two different names to distinguish whether the specified ArrayList initialization capacity for expansion when the latter is different.
3. Specify the constructor initializes a set of
This configuration is a set of incoming Collection, i.e. using the Collection initialized with the elements ArrayList object, as follows:
public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
Note: This will throw NPE if the Collection is empty, it is necessary to initialize the sentence before empty.
The principle Expansion
It analyzes above ArrayList constructor, but how do ArrayList dynamic expansion of it?
We can start from the add () method for analysis (addAll () method is similar, not analyzed separately), as follows:
// size ArrayList (including the number of elements) Private int size; // the specified element to the end of the List public Boolean the Add (E E) { ensureCapacityInternal (size +. 1); // Increments ModCount !! elementData of [size ++ ] = E; return to true ; }
It can be seen in the add () method executes, it will first execute ensureCapacityInternal () method:
private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }
The method first calculates capacity required by calculateCapacity minCapacity array method, and then determine whether to execute Grow () Method:
// default initial capacity Private static Final int DEFAULT_CAPACITY = 10 ; Private static int calculateCapacity (Object [] elementData of, int be minCapacity) { // here only in the use of initialization constructor with no arguments, perform a first time and add method ( the capacity is initialized to 10) IF (== elementData of DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max (DEFAULT_CAPACITY, be minCapacity); } return be minCapacity; } Private void ensureExplicitCapacity ( int be minCapacity) { ModCount ++ ; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
Here can be seen that, if the initial value elementData DEFAULTCAPACITY_EMPTY_ELEMENTDATA, i.e. initialized using the ArrayList constructor with no arguments, the default initial capacity of 10.
If the capacity is larger than the size of the original array minCapacity desired length (i.e., the original length of the array is not enough) is performed Grow () method (an array in expansion):
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: elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
The new computing capacity size:
int newCapacity = oldCapacity + (oldCapacity >> 1);
It can be seen, the new capacity is 1.5 times the original volume; if after expansion is 1.5 times, not yet reached the desired capacity, the direct use of the required capacity.
How the expansion of it? Use Arrays.copyOf () method creates a new array, then elements of the original array copied to the new array:
elementData = Arrays.copyOf(elementData, newCapacity);
The tracking method can be found, eventually called System.arraycopy () method:
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
ArrayList expansion Summary
1. If no initial capacity
When the first execution add () method, the length of the array 10 of default initialization, when no expansion before adding elements until capacity is equal to 10, then add the first element 11, the expansion capacity of 15 (10 + 10> > 1), and so on.
2. If the specified initial capacity initialCapacity
Before reaching the capacity of the array initialCapacity, expansion is not performed, when the capacity is equal initialCapacity if more additive elements, expansion is performed, supra expansion operation.
3. New capacity size
The default array after expansion capacity of 1.5 times the original array capacity; (may occur when using the addAll () method) if they have not reached the desired size, the desired expansion volume.
Thread safety
It can be simply understood as thread-safe: multiple threads simultaneously operating a method or variable, no problem; if problems arise, can be considered to be thread safe.
ArrayList is not thread-safe, there are mainly two:
1. When a plurality of threads to add data ArrayList (time expansion), may produce an array bounds exception (An ArrayIndexOutOfBoundsException);
2. Multiple threads traversing the same ArrayList, when there is a thread to modify it, might throw a ConcurrentModificationException.
First of add () method for analysis:
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
Note: i ++ operations are non-atomic.
Analysis of a Scene:
If an initial capacity ArrayList 1, the thread T1 and T2 simultaneously added element (the Add () method) thereto, when the second element is added, the need for expansion.
At this time if the execution timing:
1. T1, T2 detected capacity is needed
In this case, T1 and T2 are got elementData.length = 1, size = 1, if T1 before execution ensureCapacityInternal () method of expansion, the elementData.length = 2, size = 1; T2 again after execution ensureCapacityInternal () method when, because the initial size = 1, and the T1 expansion elementData.length = 2, then the expansion will not be so T2 (no execution Grow () method).
2. T1 perform the assignment and size ++ operations
After performing assignment T1 elementData [1] = XX and size ++, size increment is 2;
3. T2 performs an assignment operation (array bounds) operation and size ++
Since the step size ++ T1 operation is performed, the current size = 2, time assignment elementData [size ++] will elementData [2] performed assignment, and elementData.length = 2, the maximum index of 1, then the array will occur bounds exception (ArrayIndexOutOfBoundsException).
Scene Analysis II:
There is a the ArrayList, its traversing thread T1; T2 thread traversing them, and removing a portion of the element.
When ArrayList traverse to iterator method example, the code is as follows:
public Iterator<E> iterator() { return new Itr(); }
Creates an inner class Itr, as follows:
private class Itr implements Iterator<E> { int expectedModCount = modCount; // ... public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; Return (E) elementData of [lastRet = I]; } // check if other threads structural modifications Final void checkForComodification () { IF (ModCount =! ExpectedModCount) the throw new new a ConcurrentModificationException (); } }
And the ArrayList add (), remove () operation and the like will cause a structural modification modCount ++. Therefore: If thread T1 is traversed only ArrayList; thread T2 to the same elements ArrayList removal operation performed, will modify the values of modCount, resulting in thread T1 is modCount = expectedModCount, triggering ConcurrentModificationException!.
ArrayList Summary
1. ArrayList can be understood as "automatic expansion of array", the default initial capacity is 10, by default every time expansion is 1.5 times the original capacity;
2. expansion creates a new array element before the array and copied into the new (and therefore, to the known number of elements into the ArrayList, specified at initialization times can be avoided length expansion);
3. Thread secure and is not suitable for use in multithreaded scenarios.
Stay hungry, stay foolish.
PS: This article first appeared from the public micro-channel number.