[JDK] JDK source code analysis -ArrayList

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.

Guess you like

Origin www.cnblogs.com/jaxer/p/11105431.html