ArrayList
ArrayList基础
- ArrayList是一个动态数组
- 线程不安全
- 实现了RandomAccess接口,可快速访问
- 实现了Cloneable接口,支持clone操作
- 实现了Serializable接口,支持序列化
ArrayList初始化
ArrayList的构造方法
//存放初始化的数组
transient Object[] elementData;
//默认的空数组,在传参构造方法中使用
private static final Object[] EMPTY_ELEMENTDATA = {};
//默认的空数组,在无参构造方法中使用
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 无参构造方法,不设置ArrayList初始大小
*/
public ArrayList() {
//设置当前的数组为空数组,在add的时候才进行初始化
//使用DEFAULTCAPACITY_EMPTY_ELEMENTDATA这个参数
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 有参构造方法,指定ArrayList初始大小
* @param: initialCapacity 数组大小
*/
public ArrayList(int initialCapacity) {
//指定的数组大小大于
if (initialCapacity > 0) {
//创建一个大小为initialCapacity的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) { //如果指定的大小等于0
//设置当前数组为空数组,在add的时候才进行初始化
//使用EMPTY_ELEMENTDATA这个参数
this.elementData = EMPTY_ELEMENTDATA;
} else {//这里为initialCapacity小于0
//抛出异常,终止初始化
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
}
}
/**
* 有参构造方法,传入一个集合,将其复制进新的集合中
* @param: c 准备复制的集合
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
//如果集合c的长度不为0
if ((size = elementData.length) != 0) {
//由于上面elementData设置为了c.toArray,这里防止c.toArray后返回的不是object
//可以去这个地址查看bug(https://bugs.openjdk.java.net/browse/JDK-6260652)
if (elementData.getClass() != Object[].class){
//原数组复制到新数组
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
} else { //如果为长度为0,进入这里
//设置为空数组
this.elementData = EMPTY_ELEMENTDATA;
}
}
为什么elementData参数要设置为transient
- 为了让elementData这个参数不会被序列化
为什么要设置两个一样的参数(EMPTY_ELEMENTDATA,DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
- EMPTY_ELEMENTDATA这个参数在JDK1.7及之前的版本中使用
- DEFAULTCAPACITY_EMPTY_ELEMENTDATA这个参数在JDK1.8中加入,主要负责性能上的优化,在一定程度上减少了空数组的存在,降低内存消耗
ArrayList序列化
ArrayList实现了Serializable接口,并重写了序列化方法
因为elementData参数设置了transient,避免了序列化,所以ArrayList通过以下两个方法来进行序列化
- writeObject(java.io.ObjectOutputStream s) -> 序列化,将size和element写入ObjectOutputStream中
//一个全局变量,记录ArrayList的修改次数
protected transient int modCount = 0;
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
// Write out element count, and any hidden stuff
// 保存一下当前ArrayList的修改次数
int expectedModCount = modCount;
s.defaultWriteObject();
// 写入当前数组大小
s.writeInt(size);
// 写入所有的数组元素
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
//这里判断,在这个方法运行期间是否有其他线程操作了list
//如果modCount的值与上面保存的不一样,表示list结构被修改了,抛出异常
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
- readObject(java.io.ObjectInputStream s) -> 反序列化,从ObjectInputStream获取size和element,恢复elementData
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// 读取size
s.defaultReadObject();
s.readInt();
//如果数量大于0,才进行读取参数
if (size > 0) {
// like clone(), allocate array based upon size not capacity
SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, size);
Object[] elements = new Object[size];
// 读取elements
for (int i = 0; i < size; i++) {
elements[i] = s.readObject();
}
elementData = elements;
} else if (size == 0) { //如果大小等于0,那么直接赋值为空数组
elementData = EMPTY_ELEMENTDATA;
} else { //如果小于0,则抛出异常,反序列化失败
throw new java.io.InvalidObjectException("Invalid size: " + size);
}
}
ArrayList取值
- ArrayList通过数组的索引取值,传入index值,返回当前位置的元素
/**
* 对外开放的get方法
* @param: index 数组索引
*/
public E get(int index) {
//判断index的值是否超出了size值
//如果索引大于了数组长度,抛出异常
Objects.checkIndex(index, size);
return elementData(index);
}
/**
* 对内真正的get方法
*/
E elementData(int index) {
//根据index直接在数组查找,并根据传入的类型E进行返回
return (E) elementData[index];
}
- Objects.checkIndex()
//Objects类
@ForceInline
public static int checkIndex(int index, int length) {
return Preconditions.checkIndex(index, length, null);
}
//Preconditions类
@HotSpotIntrinsicCandidate
public static <X extends RuntimeException>
int checkIndex(int index, int length,
BiFunction<String, List<Integer>, X> oobef) {
//如果index超出了长度,则抛出异常
if (index < 0 || index >= length)
throw outOfBoundsCheckIndex(oobef, index, length);
return index;
}
ArrayList新增
ArrayList新增方法
/**
* 对外开放的新增方法
* @param: E e 新增的元素
*/
public boolean add(E e) {
//新增一次ArrayList操作次数
modCount++;
//调用真正的新增方法
add(e, elementData, size);
return true;
}
/**
* 真正的新增方法
* @param: E e 新增的元素
* @param: elementData ArrayList数组
* @param: s 当前数组的元素个数
*/
private void add(E e, Object[] elementData, int s) {
//如果size等于数组的长度了,进行扩容
//这里还有一个作用,当数组长度为0的时候,进行数组初始化
//默认创建空数组,优化了空间利用率
if (s == elementData.length)
//调用扩容方法
elementData = grow();
//设置size位置的值为新增元素
elementData[s] = e;
//size++
size = s + 1;
}
/**
* 在指定位置新增
* @param: index 数组索引
* @param: element 新增元素
*/
public void add(int index, E element) {
//index值进行校验,判断是否符合规定
rangeCheckForAdd(index);
//ArrayList操作次数+1
modCount++;
final int s;
Object[] elementData;
//数组扩容
if ((s = size) == (elementData = this.elementData).length){
elementData = grow();
}
//数组复制,将要插入的元素的位置空出来
//将index位置后的元素都向后移动1位
//第一行为原数组,原数组起始位置
System.arraycopy(elementData, index,
//新数组,新数组起始位置
elementData, index + 1,
//复制的长度
s - index);
//数组元素替换
elementData[index] = element;
//数组长度+1
size = s + 1;
}
/**
* 对新增的参数索引进行校验
* @param: index 数组索引
*/
private void rangeCheckForAdd(int index) {
//如果index大于当前数组容量,或index小于0
if (index > size || index < 0)
//抛出数组越界异常
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
ArrayList扩容
/**
* 无参扩容方法,在内部调用有参构造方法
*/
private Object[] grow() {
return grow(size + 1);
}
/**
* 有参扩容方法,内部调用真正的扩容方法
* @param: minCapacity 数组需要的最小容量
*/
private Object[] grow(int minCapacity) {
//调用数组复制方法,newCapacity(minCapacity)返回新数组的长度
return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity));
}
/**
* 真正的扩容方法
* @param: minCapacity 数组需要的最小容量
*/
private int newCapacity(int minCapacity) {
//保存原始的数组长度
int oldCapacity = elementData.length;
//新数组长度等于旧的长度 + (旧长度/2)
//如果oldCapacity为奇数,则(oldCapacity >> 1)为oldCapacity/2-1
//如果oldCapacity为偶数,则(oldCapacity >> 1)为oldCapacity/2
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果新长度小于了目前需要的最小长度
if (newCapacity - minCapacity <= 0) {
//如果当前为空数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
//DEFAULT_CAPACITY = 10
//返回DEFAULT_CAPACITY或minCapacity中最大的那个
//数组初始化也走这里
return Math.max(DEFAULT_CAPACITY, minCapacity);
//如果minCapacity小于0,则表示数据不符合规则
if (minCapacity < 0)
//数组为负数,数组越界,抛出异常
throw new OutOfMemoryError();
return minCapacity;
}
//MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
//如果新长度小于数组最大阈值,则直接返回
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
//如果大于数组阈值,计算返回结果
: hugeCapacity(minCapacity);
}
/**
* 如果数组的新长度大于数组阈值,执行这个方法
* @param: minCapacity 数组需要的最小容量
*/
private static int hugeCapacity(int minCapacity) {
//小于0,数组越界
if (minCapacity < 0)
throw new OutOfMemoryError();
//如果数组需要的值大于(MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8)
return (minCapacity > MAX_ARRAY_SIZE)
//返回Integer的最大值
? Integer.MAX_VALUE
//返回MAX_ARRAY_SIZE
: MAX_ARRAY_SIZE;
}
ArrayList更新
/**
* ArrayList更新
* @param: index 数组索引
* @param: element 更新的元素
*/
public E set(int index, E element) {
//验证index是否符合规定
Objects.checkIndex(index, size);
//将原始值保存
E oldValue = elementData(index);
//新值替换旧值
elementData[index] = element;
return oldValue;
}
ArrayList删除
/**
* ArrayList删除 - 根据索引删除
* @param: index 数组索引
*/
public E remove(int index) {
//判断索引是否符合规则
Objects.checkIndex(index, size);
//保存一个删除前的数组
final Object[] es = elementData;
//根据index获取当前位置的元素
E oldValue = (E) es[index];
//调用真正的删除方法
fastRemove(es, index);
return oldValue;
}
/**
* ArrayList删除 - 根据元素删除
* @param: o 数组元素
*/
public boolean remove(Object o) {
//获取当前list数组
final Object[] es = elementData;
//获取数组元素个数
final int size = this.size;
int i = 0;
//先查找要删除的元素是否存在
//将下面括号里的代码声明给found
found: {
//如果传入的元素为空
if (o == null) {
//循环寻找
for (; i < size; i++)
//如果找到了,退出循环
if (es[i] == null)
break found;
} else { //如果传入元素不为空
//循环查找
for (; i < size; i++)
//如果找到,退出循环
if (o.equals(es[i]))
break found;
}
//如果没找到,直接结束删除方法,返回false
return false;
}
//调用真正的删除方法
fastRemove(es, i);
return true;
}
/**
* ArrayList的真正删除方法
* @param: es 要删除的数组
* @param: i 数组索引
*/
private void fastRemove(Object[] es, int i) {
//ArrayList操作次数+1
modCount++;
final int newSize;
//数组长度大于索引
if ((newSize = size - 1) > i)
//数组index后面的元素往前移动一位
System.arraycopy(es, i+1, es, i, newSize - i);
//数组的最后一个位置置空
es[size = newSize] = null;
}