ArrayList源码分析
1.数据结构:
实现了List接口。
根据语义可知是由数组线性表;有序。
非线程安全,没有加锁,没有CAS处理。
2.全局变量
//1.默认长度
private static final int DEFAULT_CAPACITY = 10;
//2.给空实例用的数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//3.这里参照doc注释。用于默认大小的空实例的共享空数组实例。我们将其与EMPTY_ELEMENTDATA区分开来,以了解何时应该充气多少
添加第一个元素
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//4.实际存储在这个elementData中,但是不序列化这个数组
transient Object[] elementData;
//5.数组大小
private int size;
3.构造方法
(1)public ArrayList()无参构造方法:
//初始化了一个对象数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
(2)public ArrayList(int initialCapacity) 参数为int类型的一个容器初始值
//指定了对象数组的大小
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
(3)public ArrayList(Collection<? extends E> c) 参数为集合类泛型
//1.将集合转化为数组
elementData = c.toArray();
//2.判断数组长度
if ((size = elementData.length) != 0) {
//3.这里判断了一下不是数组的情况,其实这里是指第1步中使用的集合类转化数组没有成功的情况下
// 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;
}
4.核心方法 add()
(1)public boolean add(E e){
//具体实现
ensureCapacityInternal(size + 1); // Increments modCount!!
//将该元素插入数组
elementData[size++] = e;
return true;
}
分析ensureCapacityInternal(int minCapacity)方法。
private void ensureCapacityInternal(int minCapacity) {
//如果是空的,size大小为默认10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
分析ensureExplicitCapacity(int minCapacity)方法。
private void ensureExplicitCapacity(int minCapacity) {
//1.父类中的一个全局变量
modCount++;
if (minCapacity - elementData.length > 0)
//2.核心方法(扩容)
grow(minCapacity);
}
分析grow(int minCapacity) 方法,这里就是扩容。
private void grow(int minCapacity) {
//1.获取未加入新元素前数组长度
int oldCapacity = elementData.length;
//2.新的容器大小=老的容器大小+老的容器大小/2,即扩容
int newCapacity = oldCapacity + (oldCapacity >> 1);
//3.如果新的容器大小-先前容器的大小<0,则还使用之前的大小;
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//4.如果新的容器大小大于最大整数-8,则最大为最大整数。
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//5.按照新生产的容器拷贝数据
elementData = Arrays.copyOf(elementData, newCapacity);
}
(2)public void add(int index, E element) {
//1.校验Index,不能大于size
rangeCheckForAdd(index);
//2.扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//3.复制 把当前数组从第index元素起复制到 新的数组的index+1的位置,复制长度为数组长度-index(native方法)
//arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//4.在数组index位置上赋值
elementData[index] = element;
size++;
}
(3)由(1)可知末尾插入一个元素时间复杂度为O(1),由(2)可知中间插入一个元素的时间复杂度为O(n)。
5.核心方法get() 根据代码可知其查找元素的时间复杂度为O(1)
public E get(int index) {
//1.校验Index,不能大于size
rangeCheck(index);
//2.获取元素
return elementData(index);
}
//根据下标直接找到
E elementData(int index) {
return (E) elementData[index];
}
6.核心方法remov()
(1)返回删除结果
public boolean remove(Object o) {
//1.当传入的对象为空时,遍历然后移除
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) {
//1.modCount用于记录,当前集合类操作次数,在多线程情况下,发生改变时,根据内部方法调用,抛出异常
在这些线程不安全的集合中,在某些方法中,初始化迭代器时会给这个modCount赋值,如果在遍历的过程中,
一旦发现这个对象的modCount和迭代器存储的modCount不一样,就会报错。
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
//2.复制要移动的数组
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null;
}
(2)返回了删除元素
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;
}