ArrayList源码阅读
基于jdk1.8 大致从:实现/继承,基础属性,构造方法,添加元素,获取元素,删除元素几个方面
实现/继承
ArrayList实现List接口,List继承Collection接口,Collection继承Iterable接口。
package java.util;
//该接口位于java.util包下
ArrayList:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
//实现了List接口
//RandomAccess:内部任何实现也没有,称为标识接口
List:
public interface List<E> extends Collection<E>
//List接口主要是继承Collection接口
RandomAccess:
public interface RandomAccess {
}
//这就是RandomAccess接口的全部了,内部任何实现也没有
Collection
public interface Collection<E> extends Iterable<E> {
}
Iterable
public interface Iterable<T>{
}
基础属性
//ArrayList默认初始化大小
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的数组元素为:DEFAULTCAPACITY_EMPTY_ELEMENTDATA时
//添加第一个元素的时候将会扩容为默认容量大小
transient Object[] elementData;
//ArrayList容量大小
private int size;
构造方法
//无参构造
public ArrayList();
//有参构造
public ArrayList(int initialCapacity);
//有参构造,接收一个已有集合
public ArrayList(Collection<? extends E> c);
无参构造:
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//使用无参构造方式时,ArrayList的初始容量为空
有参构造:
public ArrayList(int initialCapacity) {
//实例化大小 > 0的时候
if (initialCapacity > 0) {
//按照传入实例化容量大小,实例化一个同样大小的ArrayList的缓冲区
this.elementData = new Object[initialCapacity];
//实例化大小 = 0的时候
} else if (initialCapacity == 0) {
//ArrayList的缓冲区为空
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
有参构造,接收一个已有集合:
public ArrayList(Collection<? extends E> c) {
//ArrayList的缓存数组为传入的集合的大小
elementData = c.toArray();
//判断传入的集合是否为空
//不为空
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
//如果传入的集合不是Object类型
if (elementData.getClass() != Object[].class)
//复制一个Object类型数组
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
//否则实例化为一个空的数组
this.elementData = EMPTY_ELEMENTDATA;
}
}
添加元素
ArrayList添加元素的方法为add()添加单个元素
和 addAll()添加全部元素
add():
- 按序添加:将指定元素添加至list末尾
- 添加元素到指定下标
按序添加:将指定元素添加至list末尾
public boolean add(E e) {
//要添加元素了,要看看当前集合是否有容量可以用
//先给当前集合容量+1
//就是判断当前集合是否是第一次添加元素
ensureCapacityInternal(size + 1); // Increments modCount!!
//要添加的元素添加到集合缓冲数组中,同时集合大小自增1
elementData[size++] = e;
return true;
}
ensureCapacityInternal:
private void ensureCapacityInternal(int minCapacity) {
//calculateCapacity集合缓存数组与最小容量比较,如果是第一次添加元素,将集合扩容为默认大小
//ensureExplicitCapacity
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
calculateCapacity:
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//如果集合缓冲数组为空
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//取集合默认容量大小与传入的最小容量的最大值
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
//如果不为空,返回最小容量
return minCapacity;
}
ensureExplicitCapacity
private void ensureExplicitCapacity(int minCapacity) {
//modCount:AbstractList抽象类中的属性,操作次数
//主要是为了实现fail-fast机制(快速失败机制),在遍历集合的时候对集合做结构性变化检查,即看看有没有对集合元素做删除操作
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//扩容方法,防止造成内存溢出
grow(minCapacity);
}
grow
//确保集合容量可以容纳最小容量参数指定的元素数
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容大小为1.5倍,即原数组大小+原数组大小的1/2
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);
}
hugeCapacity
//集合大小溢出问题
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//如果最小容量大于集合允许的最大容量,则最小容量为Integer的最大值 2^31 约等于 21亿
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
//list允许最大容量:Integer.MAX_VALUE - 8
//数组需要8bytes的存储大小表示数组的长度等元数据,
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
Integer.MAX_VALUE = 2^31 = 2147483648
数组对象的元数据:
- Class:只想描述对象类型的类信息的指针。
- Flag:描述对象状态的标志的集合,包括对象的散列码以及对象的形状(即对象是否为数组)。
- Lock:对象的同步信息(即对象当前是否同步)。
- Size:数组的大小。
添加元素总结:
- 首先会判断是否为第一次添加元素,如果为第一次添加元素,会将数组大小扩容为10
- 数组操作次数自增1,判断是否需要扩容;扩容类型分为默认1.5倍扩容,最小容量扩容,最大容量扩容。最大容量扩容又分为:数组最大容量和Integer最大容量
- 添加元素到list缓冲数组
添加元素到指定下标
public void add(int index, E element) {
//数组边界检查,检查要插入的下标是否超出集合边界
rangeCheckForAdd(index);
//数组结构性检查和扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//将数组从下标index开始,都往后移动一位
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//将元素插入到index位置
elementData[index] = element;
//list大小自增1
size++;
}
rangeCheckForAdd
private void rangeCheckForAdd(int index) {
//数组边界检查
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
指定下标添加元素总结:
- 数组边界检查,检查要插入的下标是否超出集合边界
- 数据结构性检查和数组扩容
- 数组移动
- 元素插入和size自增1
获取元素
ArrayList获取元素的方法为get()
get()
public E get(int index) {
//边界检查
rangeCheck(index);
//从list缓存数组中获取值
return elementData(index);
}
rangeCheck
//数组下标边界检查
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
elementData
//从list缓存数组中获取值
E elementData(int index) {
return (E) elementData[index];
}
获取元素总结:
- 数组下标边界检查
- 从list缓存数组中获取值
删除元素
ArrayList删除元素的方法是remove()
分为:
- 根据索引下标进行删除
- 根据元素对象进行删除
remove(int index):
根据索引下标删除
public E remove(int index) {
//下标边界检查
rangeCheck(index);
//操作次数自增
modCount++;
E oldValue = elementData(index);
//数组删除后要移动元素个数
int numMoved = size - index - 1;
if (numMoved > 0)
//数组从index+1及以后元素移动到index,直接覆盖
System.arraycopy(elementData, index+1, elementData, index,numMoved);
//最后一个元素置空,且数组长度减一
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
删除元素总结:
- 下标边界检查
- 操作次数自增
- 数组删除后要移动元素个数,数组从index+1及以后元素移动到index及以后,直接覆盖index下标的值
- 最后一个元素置空,且数组长度减一
remove(Object o)
根据元素对象删除
public boolean remove(Object o) {
//如果要删除的元素为空
if (o == null) {
//遍历集合
for (int index = 0; index < size; index++)
//如果查询出来为null ==
if (elementData[index] == null) {
//删除
fastRemove(index);
return true;
}
//删除元素不为空
} else {
//遍历集合
for (int index = 0; index < size; index++)
//equals方法
if (o.equals(elementData[index])) {
//删除元素
fastRemove(index);
return true;
}
}
return false;
}
fastRemove():快速删除方法
private void fastRemove(int index) {
//操作次数自增
modCount++;
//要移动元素
int numMoved = size - index - 1;
//如果不是最后一个值
if (numMoved > 0)
//移动,覆盖
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//数组最后一个元素置空(gc 回收),长度减一
elementData[--size] = null; // clear to let GC do its work
}
iterator的ArrayList内部类Itr
Itr是实现iterator
接口的ArrayList内部类
private class Itr implements Iterator<E> {
//两个游标
//下一个元素的下标
int cursor; // index of next element to return
//最后返回的元素的下标
int lastRet = -1; // index of last element returned; -1 if no such
//期望操作次数 和 操作次数 :用于检测集合的结构性变化
int expectedModCount = modCount;
//无参构造
Itr() {}
}
hashNext()
//判断是否遍历结束
public boolean hasNext() {
//下一个元素的下标与集合大小
return cursor != size;
}
next()
//获取集合的下一个元素
public E next() {
//检查集合结构性变化,就是检查期望操作次数 和 操作次数是否一致
checkForComodification();
int i = cursor;
//检查要获取元素的下标,是否超出集合大小
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
//检查要获取元素的下标,是否超出集合缓冲数组大小
if (i >= elementData.length)
throw new ConcurrentModificationException();
//更新指向下一个元素的下标游标
cursor = i + 1;
//返回当前元素,更新最后返回元素的下标游标
return (E) elementData[lastRet = i];
}
checkForComodification
//检查集合结构性变化
final void checkForComodification() {
//就是检查期望操作次数 和 操作次数是否一致
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
remove
public void remove() {
//检查要删除元素下标边界
if (lastRet < 0)
throw new IllegalStateException();
//集合结构性检查
checkForComodification();
try {
//调用ArrayList根据下标索引删除的方法
ArrayList.this.remove(lastRet);
//更新两个游标的值,和期望操作次数和操作次数的值
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
forEachRemaining
对集合中剩余的元素进行遍历输出,简单来说,如果在集合中有10个元素,先遍历输出了5个,再使用forEachRemaining
的时候只会输出5个元素。
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
//将下一个元素的下标赋值给了要输出元素的下标,从这里就可以看出来 forEachRemaining是做什么的,就是输出集合中剩下的元素
int i = cursor;
//检查输出下标边界
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
//输出下标边界检查,期望操作次数与操作次数检查
while (i != size && modCount == expectedModCount) {
//函数式接口,输出
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
//两个游标值更新
cursor = i;
lastRet = i - 1;
//集合结构性检查
checkForComodification();
}
//集合结构性检查
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}