ArrayList源码详解
必要的初始知识
System.arraycopy这个东西最好要知道一下,这是ArrayList各种操作的核心.
构造器部分
给定一个初始值,然后用这个初始值声明数组.
**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative(负数)
*/
// 用初始长度去声明一个空的列表(list),initialCapacity固初始化长度,IllegalArgumentException如果给的值不合理(负数),就抛出这个错误.
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);
}
}
不给定初始值,用默认的空数组赋值
/**
* Constructs an empty list with an initial capacity of ten.
*/
//如果不进行赋值就用一个之前声明过的数组赋值
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
给定的初始值是一个集合
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*创建一个列表(list)包含一个已经指定元素的集合,用由那个集合的迭代器按顺序返回
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();//将输入的集合化为数组--Object[]数组.
//如果输入的集合不为空
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
//对于这段话的解释https://www.cnblogs.com/gilbertbright/p/11714334.html
if (elementData.getClass() != Object[].class)
//如果输入的内容不是object类型,则要按照那个类型去进行赋值.
//original - 要复制的数组
//newLength - 要返回的副本的长度
//newType - 要返回的副本的类
//将elementData到Object数组中,即让他们的类型产生变化
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
c.toArray might (incorrectly) not return Object[] (see 6260652)是什么意思
https://www.cnblogs.com/liqing-weikeyuan/p/7922306.html 这个也是个很好的问题
本类的成员变量
private static final long serialVersionUID = 8683452581122892189L;
/**
* Default initial capacity.初始化长度.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};//如果故意在有参构造函数中传0用的数组.
/**
* 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 = {};//如果用空参构造函数,把这个作为空参构造函数的值.
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access//ArrayList的底层函数
/**
* The size of the ArrayList (the number of elements it contains).
*ArrayList的长度
* @serial
*/
private int size;
java集合-ArrayList中EMPTY_ELEMENTDATA与DEFAULTCAPACITY_EMPTY_ELEMENTDATA的区别
缩减函数
/**
* Trims the capacity of this <tt>ArrayList</tt> instance to be the
* list's current size. An application can use this operation to minimize
* the storage of an <tt>ArrayList</tt> instance.
*/
//缩减这个ArrayList实例的容量道它的最优大小.一个程序可以用这个操作去缩小一个ArrayList实例的大小.
public void trimToSize() {
modCount++;
if (size < elementData.length) {
//如果size为0就给他变回空数组,如果不是空数组,则复制它的数组,截掉那部分值为0的尾部
1,2,3,4,0,0,0->1,2,3,4
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
计算最小扩容长度函数(预备)
/**
* Increases the capacity of this <tt>ArrayList</tt> instance, if
* necessary, to ensure that it can hold at least the number of elements
* specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity//最小需求容量
*/
//增加这个ArrayList的容量,如果是不必要的,保证这个ArrayList中的元素数量是最小容量.
//在扩容的时候使用的函数,保证扩容增长的长度是最小的长度.
public void ensureCapacity(int minCapacity) {
//最小扩展量
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
//如果不是固定的那个空数组,最小的扩展就是0
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
//如果传入的数比默认的空数组大,也被建议先采用默认值.
: DEFAULT_CAPACITY;
//如果最小需求容量大于最小扩展值(最小扩展值实际上只在空ArrayList被第一次加入元素的时候才有用处[用处是直接把容量从0加到10],其他时候没用)
if (minCapacity > minExpand) {
//确定准确的ArrayList容量是什么.
ensureExplicitCapacity(minCapacity);
}
}
如上函数被addAll()用到的
如下函数是被add()用到的
//确定这个最小容量的大小.
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//如果elementData还是最小初始数组
<---2.被 ensureCapacityInternal调用,返回值给ensureExplicitCapacity
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//看看这个最小容量大还是规定的默认长度(10)大,返回大的那个
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//如果
private void ensureCapacityInternal(int minCapacity) { <---1.被add()方法调用
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//确定准确的ArrayList容量是什么.
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
扩容函数
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
//增加容量去确保ArrayList拥有至少由最小容量给定的容量.
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;//老板长度
int newCapacity = oldCapacity + (oldCapacity >> 1);//old+old/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);
}
增加函数
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
上面三个函数要一同看
当调用一个add()
方法的时候首先运行的是 ensureCapacityInternal(size + 1);
方法,判断ArrayList是否需要扩容.在这个函数中会先调用private static int calculateCapacity(Object[] elementData, int minCapacity)
函数,去对增加的数量进行判断.
对于用空参构造函数构造的ArrayList,为了节省空间实际上使用的是一个空数组,并没长度(并不是10).所以在这个函数中要判断增加的元素数量是否大于10(也就是ArrayList的默认长度),如果不大于10,用10作为容量,反之用传入的那个长度作为容量.
然后调用private void ensureExplicitCapacity(int minCapacity)
在这里又进行了另一次判断,这里要注意传入的容量(minCapacity)未必大于ArrayList的现有长度.,所以在这里要有一次判断判断是否应该要调用grow()方法进行扩大.
一个toArray()方法
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
//用传入的值去更改这个ArrayList中的所有内容
//Arrays.copyOf复制指定的数组,用空值截断或填充(如有必要),以便复制具有指定的长度。
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;//如果进来的数组长度大于ArrayList长度,则将数组中最后一个设置为空.
return a;//返回数组
}
一个get()方法
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}//返回index位置上的索引的值
get()方法是由两个函数组成.目标是更加的简洁
set()方法也是大体如此
public E set(int index, E element) {
rangeCheck(index);//确定没有超出边界
E oldValue = elementData(index);//保留原先的值
elementData[index] = element;//覆盖新的值
return oldValue;//将原先的值返回
}
增加函数
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!//判断是否需要扩容,上面讲过了.
System.arraycopy(elementData, index, elementData, index + 1,
size - index);//从index位置上的元素开始将后面的所有元素(总长度size-索引位置index)挪到index+1的位置上
elementData[index] = element;//把值放在index索引处
size++;//长度计数器增加.
}
减小函数
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);//取出原先的值
int numMoved = size - index - 1;//因为index上的值消失,所以实际上是从index+1开始的,也正是因此要多减一个
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);//从index+1开始复制,一直到数组解为,粘贴到index开始的位置上
//此时会有两个最后一个元素,让他为空
elementData[--size] = null; // clear to let GC do its work
//如果一个对象所有的引用都为空,则与GC-root断链,会被JVM标记,然后清除.
return oldValue;
}
add All()
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();//将传入的集合化为数组,每个集合中的元素都会被擦除成Object类型.
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);//从a(传入数组)的0位置复制该数组长度,从size开始粘贴到ArrayList维护的底层数组.
size += numNew;
return numNew != 0;
}
这个本质来讲没啥区别,但是重点是用到了ensureCapacityInternal(size + numNew);
这个方法,在上面的模块中,这个一直没用用到.请回到上面去看一下.注意minCapacity
为最小需求容量.上面可能有些出入,请记住这个意思.
如上就是我们经常用到的ArrayList方法的详解.
还有一部分方法,大多原理和如上几个方法重复,或者平时基本不用.就不再写出来了.