[Container] ArrayList Java source code parsing

This is the first article in 2020! Today, you learn it?

List ArrayList is the most common implementation class, today we source from the point of view to analyze this class.

First, the basic structure

First, let's look at the inheritance ArrayList, which is a UML diagram:

file

For ArrayList, so we usually use:

List<Object> list = new ArrayList<>();
复制代码

Here's a brief look at the interface summary:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
	// ...
}
复制代码

ArrayList AbstractList inherited abstract class that implements List, RandomAccess, Cloneable and serial interfaces.

It should be noted that it RandomAccess interface that there is no way to achieve this means that the interface supports fast random access (subscript access). Fast random access speed> iterator traversal speed.

Fast random access speed is fixed, such as the ArrayList set (i, e), get (i) or the like, the time complexity is O (1)


Second, property

View ArrayList source, we see it mainly has the following properties that describe these attributes are marked on the following:

// 默认初始化容量
private static final int DEFAULT_CAPACITY = 10;
// 用于空实例的共享空数组实例
private static final Object[] EMPTY_ELEMENTDATA = {};
// 用于默认大小空实例的共享空数组实例,与 EMPTY_ELEMENTDATA 的区别在于前者知道当第一个元素插入时该如何扩充数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 存储这个 ArrayList 元素的数组。ArrayList 的容量就是这个数组的长度。任何空 ArrayList,如果其 elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA,则当添加第一个元素时,ArrayList 会扩容到 DEFAULT_CAPACITY 大小。
transient Object[] elementData;
// ArrayList 的大小,指元素的个数
private int size;
// 数组可分配的最大大小
// 某些虚拟机在一个数组里保留了一些标题字。尝试分配更大的数组可能会导致 OutOfMemoryError
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
复制代码

As can be seen from the basic properties of the underlying ArrayList is based on an array of!

That Object[] elementDatathis property

It should be noted that:

  • size is the actual number of elements, i.e. list.size ()
  • elementData.length a length of the array of storage elements, typically> = size of

Third, the three constructors

Usually use ArrayList, I personally used to this structure:

List<Integer> list = new ArrayList<>();
复制代码

In fact, there are three the ArrayList Constructor: default constructor with constructor initializes capacity parameter with a set of parameters constructor

  • The default constructor

    // 使用默认容量 10 构造一个空的 List 
    public ArrayList() {
      // 这里可以看到使用了属性 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
    	this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    复制代码
  • With initial capacity constructor

    public ArrayList(int initialCapacity) {
            if (initialCapacity > 0) {
                // 构建指定容量的数组
                this.elementData = new Object[initialCapacity];
            } else if (initialCapacity == 0) {
                // 使用 EMPTY_ELEMENTDATA
                this.elementData = EMPTY_ELEMENTDATA;
            } else {
                // 负数,抛出 IllegalArgumentException
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            }
        }
    复制代码
  • Set of parameters to the constructor

    // 构造一个包含指定集合元素的 list,元素顺序按照集合的迭代器返回的顺序
    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;
        }
    }
    复制代码

EMPTY_ELEMENTDATA 与 DEFAULT_EMPTY_ELEMENTDATA

The difference is that, when the former is used, when adding a first element, elementData.length = 1; and when using the latter method will be called ensureCapacityInternal the elmentData.length = DEFAULT_CAPACITY = 10

Note that expansion occurs in real time to insert elements! Before using the two constructors, elementData empty array.

The following highlights summarize the expansion mechanism ArrayList.


Fourth, the expansion mechanism

4.1 Why should the expansion?

Personal understanding is that when initializing ArrayList, JVM does not know how much data will be stored into the program, but they must array to declare its capacity at initialization, thus causing a problem, at the latest when the first element is inserted must be specified the capacity of the array size, in order to initialize the array receiving element. (ArrayList actually does exactly that!)

So, in the end you need to know how the program how much capacity it? 1 or 100w?

The best way is to be a dynamic expansion!

ArrayList on the realization of this expansion feature, we can take the initiative to call the method expansion, but also allows ArrayList own expansion. Below is concerned about the ArrayList expansion is how to achieve specific. This explanation is mainly for analyzing source code.

Achieve expansion of 4.2

ArrayList expansion is achieved by the following piece of code, I've added the comment:

    // 扩容 ArrayList 实例,使其至少能放下 minCapacity 个元素
    public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            // any size if not default element table
            ? 0
            // larger than default for default empty table. It's already
            // supposed to be at default size.
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

	// 确保明确的容量
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++; // 注意,这里递增了 modCount

        // overflow-conscious code 
        if (minCapacity - elementData.length > 0)
            grow(minCapacity); // 正式扩容
    }

    // 扩容,以确保该 elementData 数组的大小至少能容纳 minCapacity 个元素
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1); // 即扩容到 1.5 倍,等价于 n = o + o/2
        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);
    }

	// 可以扩容的最大容量,当想要扩容的容量大于 MAX_ARRAY_SIZE 时调用
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
复制代码

You can see, the actual expansion method is: grow (int minCapacity). The most critical line of code is:

 int newCapacity = oldCapacity + (oldCapacity >> 1); // 等价于 n = o + o/2
复制代码

As used herein bit computing, it's very fast!

Summary, ArrayList expansion is that each expansion to the original size of 1.5 times.

So, when it carried out the expansion?

When 4.3 expansion

From public void ensureCapacity(int minCapacity)it can be seen, we can take the initiative to call the method for expansion.

Further, when it is called add / addAll other additive elements, also called the ArrayList internal expansion process ( ensureCapacityInternal) to active expansion. Method to add an example:

    // 追加元素 e 到 list 的末尾
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 我们主要,ensureCapacityInternal 这个方法会调用 ensureExplicitCapacity 这个方法,后者中一行代码 modCount++
        elementData[size++] = e;
        return true;
    }
复制代码

In the above expansion method, and the like add process will be mentioned later, we are also the underlying array of methods. After all, it is based on an array of ArrayList thing! Here we look at the underlying array of specific methods What does it mean?


Fifth, the underlying method Array

We can see the emergence of two methods related to the operation of the array copy of the source code in ArrayList, a method is Arrays.copyOf Arrays class, a method is System.arraycopy observe the source, the former is based on the latter:

5.1 Arrays.copyOf method

// 复制指定的数组,截断或填充 null 值使其达到指定长度
// 返回值与 original 数组具有相同的值
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
  			// 调用了 System 类的 arraycopy 方法
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
}
复制代码

5.2 System.arraycopy method

public static native void arraycopy(Object src,  int  srcPos,
                                    Object dest, int destPos,
                                    int length);
复制代码

Explain it in detail:

  • Array of the specified position from an original, copy data to the destination array. SrcPos copy starting position of the original array, destPos copied to the target position array length copy length, i.e., copied from the original array to srcPos srcPos + length-1, copied to the destination array to destPos destPos + length-1
  • If the original and destination array point to the same array object, it is first copied to a temporary array, the temporary array is then copied to the original array
  • Parameters: src - the original array, srcPos - the start position of the original copy of the array; dest - number of array elements to be copied - the target array, destPos - copied to the start position of the target array, length

Sixth, a problem

Ice to see Ao article, which referred to the question: Use public ArrayList(int initialCapacity)when this constructor to build ArrayList, when to initialize an array?

Can be seen by the source, when the constructor is called, initialize the array!

this.elementData = new Object[initialCapacity];
复制代码

However, we need to pay attention! This time can not call List.get (I) and the set (i, e) method.

Ok? Ok? Ok? ! !

The reason is simple, list of size 0 or at this time!

The ArrayList add elements, elements of the operation will be to get a boundary value (the relationship between the index and size of) the array to be checked.

For example get (i) Method:

public E get(int index) {
    rangeCheck(index);
    return elementData(index);
}
复制代码

rangCheck method examines the relationship between the index and the size, if index> = size, will throw an exception:

private void rangeCheck(int index) {
	if (index >= size)
		throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
复制代码

such as:

public static void main(String[] args) {
        List<Integer> list = new ArrayList<>(10);
        Integer a = list.get(0);
        System.out.println(a);
}
复制代码

Running this code will be reported the following exception:

Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at java.util.ArrayList.rangeCheck(ArrayList.java:657)
	at java.util.ArrayList.get(ArrayList.java:433)
	at com.aegis.MapTest.main(MapTest.java:13)
复制代码

Below we summarize in the ArrayList common methods:


Seven common method

The following is a summary of the public ArrayList source method, the current does not involve the relevant portion 8 JDK.

7.1 Basic

void trimToSize()

  • ArrayList modified example of the capacity of the current list.size (), i.e., the current number of elements. This method can be used to minimize the capacity of such ArrayList
public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
        elementData = (size == 0)
          ? EMPTY_ELEMENTDATA
          : Arrays.copyOf(elementData, size);
    }
}
复制代码

int size()

  • Returns the size of the ArrayList. It refers to the size of specific elements, rather than the size of the array elementData!
public int size() {
	return size;
}
复制代码

boolean isEmpty()

  • If the list is no element returns true
public boolean isEmpty() {
    return size == 0;
}
复制代码

boolean contains(Object o)

  • Determine whether to include an element; call indexOf method to determine
public boolean contains(Object o) {
        return indexOf(o) >= 0;
}
复制代码

int indexOf(Object o)

  • Returns the index position of the specified element occurs; if not, returns -1
public int indexOf(Object o) {
    if (o == null) {
        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;
}
复制代码

int lastIndexOf(Object o)

  • Returns the last location of the specified element occurs; if not, it returns -1
  • Actually reverse iterate
public int lastIndexOf(Object o) {
    if (o == null) {
        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;
}
复制代码

Object clone()

  • Returns an array of shallow copy
  • Shallow copy here is not referring to List itself, but the element List. Arrays.copyof shallow copy method here is caused.
public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // 这是不应该发生的,因为 ArrayList 实现了 Clone 接口,即 Cloneable 的
            throw new InternalError(e);
        }
    }
复制代码

public void clear()

  • Removes all elements from a list
public void clear() {
    modCount++;

    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}
复制代码

7.2 toArray

Object[] toArray()

  • Returns the list includes all of the elements in the array, the order of elements in the order returned Iterator prevail.
  • This method is the array collection and conversion of bridge
public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}
复制代码

T[] toArray(T[] a)

  • To specify the type comprising an array of return in this list, all array elements
  • If the array is capable of receiving a list of the element, the element will be copied to the array; otherwise copied into a new array element, the array type determined by the designated its runtime.
  • If the array is greater than a list, then a [list.size ()] = null
public <T> T[] toArray(T[] a) {
	if (a.length < size)
  	// Make a new array of a's runtime type, but my contents:
    return (T[]) Arrays.copyOf(elementData, size, a.getClass());
  System.arraycopy(elementData, 0, a, 0, size);
  if (a.length > size)
  	a[size] = null;
  return a;
}
复制代码

7.3 get/set/remove

E get(int index)

  • Get list of elements in the specified location
public E get(int index) {
  	// 范围检查,如果 index 超出 list 大小,就抛出 IndexOutOfBoundsException
    rangeCheck(index);

    return elementData(index);
}
复制代码

E set(int index, E element)

  • Alternatively the specified elements in an element at the element at the specified index
  • Return to the original elements
public E set(int index, E element) {
  	// 检查边界
    rangeCheck(index);

  	// 旧的元素
    E oldValue = elementData(index);
    // 设置 index 处为新元素 element
  	elementData[index] = element;
    return oldValue;
}
复制代码

boolean add(E e)

  • Add an element at the end of the list. Returns true if successful
public boolean add(E e) {
    // 检查容量 - 即是否需要扩容
  	ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 将 size 位置设为
  	elementData[size++] = e;
    return true;
}
复制代码

void add(int index, E element)

  • Insert element at the specified location, and the original elements and all subsequent backward an element (index ++)
public void add(int index, E element) {
  			// 检查边界
        rangeCheckForAdd(index);

  			// 检查容量 - 如需要则扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
  			// 将数组中 index 位置开始的数据,拷贝到 index+1 到 size 位置 
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
  			// 将 index 位置设为新元素
        elementData[index] = element;
        size++;
    }
复制代码

E remove(int index)

  • Removes the specified location of the elements, the elements behind the left one (index--)
  • Returns the element deleted
public E remove(int index) {
  	// 检查边界
    rangeCheck(index);

  	// 修改 modCount,以便 fail-fast 机制生效
    modCount++;
  	// 获取 index 位置的元素 -- 即要被删除的元素
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
      	// 向左移动元素
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    // 将原先的最后一位设为 null
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}
复制代码

boolean remove(Object o)

public boolean remove(Object o) {
    if (o == null) {
      	// 要删除的对象为 null,则寻找第一个 null 值
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
              	// 调用 fastRemove
                fastRemove(index);
                return true;
            }
    } else {
      	// 
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}
复制代码

7.4 set associative operation

boolean addAll(Collection<? extends E> c)

  • The end of the set of all the elements in the order returned by the iterator is added in the list
  • If the list is changed, it returns true
public boolean addAll(Collection<? extends E> c) {
    // 集合转数组
    Object[] a = c.toArray();
    int numNew = a.length;
    // 确保容量
    ensureCapacityInternal(size + numNew);  // Increments modCount
    // 从数组拷贝元素到 list 中
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}
复制代码

boolean addAll(int index, Collection<? extends E> c)

  • From a specified starting position c in the set of insert elements, the position of the new elements is set according to the iterator returned in order, until all elements of the insertion is completed. Before and after the shift elements.
public boolean addAll(int index, Collection<? extends E> c) {
  	// 边界值检查
    rangeCheckForAdd(index);

    // 要插入的元素和数量
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount

    int numMoved = size - index;
    if (numMoved > 0)
        // 首先,后移之前的元素
        System.arraycopy(elementData, index, elementData, index + numNew,
                         numMoved);
    // 然后插入新元素
    System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;
    return numNew != 0;
}
复制代码

boolean removeAll(Collection<?> c)

  • Delete Collection c in the same element in the list. Returns true if the list changes
public boolean removeAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, false);
}
复制代码

boolean retainAll(Collection<?> c)

  • Delete list does not include all of the elements in the collection c
public boolean retainAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, true);
}
复制代码

7.5 iterator

List returned iterator is ListIterator interface than the default Iterator, more out of hasPrevious and previous methods.

There are two ListIterator ArrayList implementation classes: ListItr and Itr. The difference is that the former has modified elements of the set and add methods

listIterator

  • Returns a list iterator of the elements in this list, the element beginning at the specified location.
public ListIterator<E> listIterator(int index) {
    if (index < 0 || index > size)
        throw new IndexOutOfBoundsException("Index: "+index);
    return new ListItr(index);
}
复制代码
  • Returns the list of elements in a list iterator elements starting from the position 0
public ListIterator<E> listIterator() {
    return new ListItr(0);
}
复制代码

iterator

  • Return to this list iterator
public Iterator<E> iterator() {
    return new Itr();
}
复制代码

ListItr and inner class Itr

  • Itr is the difference between the latter and having ListItr method to modify and add elements set

7.6 subList

  • This returns the list of sub-views, [fomIndex, toIndex)
  • Note that the sub-list subList return of the original ArrayList and pointing to the same array. That is, the operation of sub-list will be the ArrayList synchronous mapping in the original, and vice versa.
public List<E> subList(int fromIndex, int toIndex) {
    subListRangeCheck(fromIndex, toIndex, size);
    return new SubList(this, 0, fromIndex, toIndex);
}
复制代码

Eight, summary

  • Based array element orderly, supports fast random access, that access index.

  • To achieve a dynamic expansion mechanism, each expansion will increase to 1.5 times

  • Elements may be null

  • Thread unsafe. In the case of multi-threading, please use the ArrayList method Collections.synchronizedList package is thread-safe version, or the direct use of Vector old


References:

Java collection of in-depth understanding of AbstractList - CSDN wipe Heart

Depth understanding of the Java Collections ArrayList - CSDN wipe Heart

ArrayList source code parsing - JavaGuide

ArrayList details - skywang12345

"Easing the bit interviewer" series - ArrayList - Ao propionate


Written on the back

This article was written for nearly a week, looked at the source code, read the article several well-known bloggers, are going to speak on the basis of something that they want concise, until today (2020 Great Year) The main contents of the morning and only then finished. Carefully write an article really hard. The following HashMap, will be more difficult! Please look forward to it ha ha ha!

Recently infectious disease is very serious, I hope everyone can feel at ease to be at home, pay attention to safety! I wish you all a Happy New Year! Every family safe! I hope we can take advantage of these days to read a book, learning to learn, I am afraid that the economic situation in 2020 will be more severe, employment, too!

let's work hard together!

- At 2020.01.26 12:54

Guess you like

Origin juejin.im/post/5e2d5e96f265da3e2170aa7a