ArrayList源码解析
属性解析
序列化id
private static final long serialVersionUID = 8683452581122892189L;
初始化默认的容量
private static final int DEFAULT_CAPACITY = 10;
用于指定大小arrayList对象的空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
用于创建默认大小ArrayList对象的空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
存储ArrayList元素的数组缓冲区。
transient Object[] elementData; // non-private to simplify nested class access
ArrayList数组的长度默认为0
private int size;
构造方法解析(三个)
无参构造
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
构建一个默认长度的ArrayList对象
有参构造一:
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);
}
}
根据传递过来的参数创建一个相应容量的ArrayLIst对象
有参构造二:
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;
}
}
将传递过来的集合转成数组,再将数组内容复制到ArrayList内的数组属性上
构造总结:arrayList的构造方法就是初始化储存数据的容器,其实本质上就是一个叫elementData的数组
注意:在有参方法一和无参构造中并不会对size属性实例化,只有在有参构造二中如果所传递的集合不为空集合的时候会初始化size属性
常用方法解析
add()方法解析
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
再调用add方法时
1:调用ensureCapacityInternal()方法处理容量问题
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
首先根据elementData 判断是否是无参构造创建的,是的话 获取DEFAULT_CAPACITY与minCapacity之间较大的那个值 并用较大的值作为参数调用ensureExplicitCapacity方法(不是无参构建的直接用minCapacity调用)
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
这个方法首先记录下整个集合修改的次数: modCount++;
在判断minCapacity 是否大于当前集合的容量是的话调用grow()方法进行扩容
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);
}
由代码可知:每次扩容默认增加当前集合的一半,但是当扩容一半也小于需要的容量时,则扩容至需要的容量
而集合的最大容量则为MAX_ARRAY_SIZE (Integer.MAX_VALUE - 8)当超出这个容量时调用hugeCapacity方法
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
判断minCapacity 是否大于MAX_ARRAY_SIZE 是的话返回Integer.MAX_VALUE至grow方法,在进行扩容。
结束所有容量方法之后将E赋值到elementData数组,并对size自增;
有参add(int index, E element)方法
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
这个方法时添加数据到指定下标,那么首先就需要调用rangeCheckForAdd方法对下标进行检查
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
判断index是否合规,不合规报错;
接着调用容量处理方法(详情请看add方法解析)
再调用System.arraycopy将elementData数组index后的数据集体后移
最后是将值赋给elementData的指定下标处并将size自增。
删除相关方法解析
remove(int index)解析:删除指定下标数据
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
删除方法:判断下标合规之后,将指定下标后的所有数据前移1,然后将最后一个赋值为null
remove(Object o)解析:删除指定对象
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
直接遍历整个数组找到相应的对象,然后将下标传到fastRemove(int index)方法,进行删除;
总结:
1:ArrayList其本质就是一个elementData数组(允许存放null);其默认容量为10,每次扩容(grow)会默认新增当前容量的一半当新增一半不够时增加到本次添加后的数量。其最大容量是int的最大值-1-8;但是到了这个值之后还可以再增加八个容量
2:增加就是再数组内添加数据,指定下标增加:将指定下标及指定下标以后的数据往后移(System.arraycopy),并将数据添加到指定下标
3:删除数据本质就是将指定下标后的数据迁移一,然后将最后一个赋值为null