目录
目录
前言
一、ArrayList继承关系及全部源码
1、继承关系
2、构造函数及方法列表
二、分段解读1.ArrayList属性
2.构造函数
2.1 无参构造函数
2.2 参数为数字类型的构造函数
2.3、参数为集合的构造函数
3、添加方法
3.1 add(E e)
3.2 add(int index, E element)
3.3 addAll(Collection c)
3.4 addAll(int index, Collection c)
4、删除
4.1 remove(int index)
4.2 remove(Object o)
4.3 removeAll(Collection c)
4.4 retainAll(Collection c)
4.6 removeIf(Predicate filter)
4.6 removeRange(int fromIndex, int toIndex)
4.7 clear()
6、查询
6.1 get(int index)
6.2 indexOf(Object o)
6.3 lastIndexOf(Object o)
6.4 contains(Object o)
7、其他
7.1 trimToSize()
7.2 ensureCapacity(int minCapacity)
7.3 size()
7.4 isEmpty()
7.5 clone()
7.6 toArray()
7.7 toArray(T[] a)
7.8 iterator()
总结
前言
为了复习Java集合相关知识点,对相关Java 集合源码进行走读学习,本篇文章记录对ArrayList(JDK1.8)源码走读的记录
一、ArrayList继承关系及全部源码
1、继承关系
2、构造函数及方法列表
二、分段解读
1.ArrayList属性
2、构造函数及方法列表
二、分段解读
1.ArrayList属性
二、分段解读
1.ArrayList属性
//序列号
private static final long serialVersionUID = 8683452581122892189L;
//默认初始化容量
private static final int DEFAULT_CAPACITY = 10;
//空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 就是存储ArrayList元素的数组,这也就是说ArrayList用数组来存储元素的
transient Object[] elementData; // non-private to simplify nested class access
private int size;
DEFAULTCAPACITY_EMPTY_ELEMENTDATA与EMPTY_ELEMENTDATA 都是一个空数组,区别就是使用的地方不一样,DEFAULTCAPACITY_EMPTY_ELEMENTDATA属性是在无参构造函数创建对象时被使用,而EMPTY_ELEMENTDATA则使用在集合容量为0是被使用。
size 指的是当前集合中存在的元素个数
elementData.length() 指的是该容器,容量的大小
2.构造函数
2.1 无参构造函数
//空参构造函数
public ArrayList() {
//如果只是用空参的构造函数创建了一个ArrayList对象,其实此时集合容量为0,
//即elementData为一个空容量的数组
//因为DEFAULTCAPACITY_EMPTY_ELEMENTDATA就是一个空容量的数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//空参构造函数
public ArrayList() {
//如果只是用空参的构造函数创建了一个ArrayList对象,其实此时集合容量为0,
//即elementData为一个空容量的数组
//因为DEFAULTCAPACITY_EMPTY_ELEMENTDATA就是一个空容量的数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
无参构造函数创建一个容量为0的集合,此时elementData引用了DEFAULTCAPACITY_EMPTY_ELEMENTDATA对象,当第一次向ArrayList添加数据时容器会扩容至默认容量 10
2.2 参数为数字类型的构造函数
//有参构造函数,参数为创建集合时要集合容量的大小
public ArrayList(int initialCapacity) {
//判断参数是否大于0
if (initialCapacity > 0) {
//如果参数大于0,则创建指定大小的数组,并赋值给 elementData
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//如果参数等于0 则 将空数组赋值给elementData
this.elementData = EMPTY_ELEMENTDATA;
} else {
//如果参数对上边两个判断都不满足则报异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
该构造函数参数为要创建的ArrayList集合的容量大小,如果大于0则创建指定大小的数组对象,并把对象的引用赋值给成员属性elementData,如果参数为0则把EMPTY_ELEMENTDATA对象赋值给elementData,此时集合容量为0,如果参数小于0则会抛出IllegalArgumentException异常
2.3、参数为集合的构造函数
public ArrayList(Collection extends E> c) {
//将构造方法中的参数转成数组,如果参数为空则会报NullPointerException异常
elementData = c.toArray();
//elementData长度值赋给size,也就是说此时ArrayList它包含的元素数为传过来集合转换为数组的长度
//判断elementData.length是否不等于0
if ((size = elementData.length) != 0) {
//如果不等于0,并且这个数组的类型不是Object[],那么将他转化成Object[]
// c.toArray might (incorrectly) not return Object[] (see 6260652) 这句话大概的意思是c.toArray()有可能返回的不是Object[] 类型
//加上这个判断可以及转换就是为了解决这个bug
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
//如果elementData.length等于0则 elementData元素被赋值为 EMPTY_ELEMENTDATA
// replace with empty array.
// 替换为空数组。
this.elementData = EMPTY_ELEMENTDATA;
}
}
该构造函数的参数为集合,该集合的元素将会全部添加到新建的 ArrayList 实例中。构造函数中会将参数toArray转换成数组,并把对象引用赋值给成员属性elementData,接下来会对elementData.length进行判断,如果为0则说明传入的参数容量为空,则此时会将EMPTY_ELEMENTDATA赋值给elementData,此时创建的ArrayList容量为0。如果不为0,则判断 elementData.getClass() != Object[].class是否为true,如果为true则会进行转换,把数组转化成Object[]。至于为什么要进行判断转换则点击此处查看原因。
3、添加方法
3.1 add(E e)
//
public boolean add(E e) {
//ensureCapacityInternal方法是确保容器数组有空间,
//特别是传参为size+1,既能保证添加这个新元素数组不溢出,又不会导致空间浪费
ensureCapacityInternal(size + 1); // Increments modCount!!
//size为ArrayList中包含的元素数,
//数组是0从0下标开始的所以传进来的元素放在elementData[size]的位置
//size++表示元素e添加成功后,集合中的元素数又多了一个
elementData[size++] = e;
//如果添加成功则返回true
return true;
}
//参数为数字类型,指定的最小容量
private void ensureCapacityInternal(int minCapacity) {
//判断elementData与DEFAULTCAPACITY_EMPTY_ELEMENTDATA两个对象是否相等
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//如果相等,则取DEFAULT_CAPACITY与minCapacity当中的最大值
//DEFAULT_CAPACITY为10,minCapacity是动态的
//只有用空参构造函数创建的集合才会相等
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
//modCount是记录集合被修改的次数,这里的修改指的是增删改
//modCount是AbstractList中定义的字段
//集合修改次数加1
modCount++;
//防止溢出代码:确保指定的最小容量 > 集合的当前容量
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//minCapacity>elementData.length则说明容量不够需要扩容
grow(minCapacity);
}
//减去8 是因为Some VMs reserve some header words in an array 有些虚拟机在数组中保留一些头字
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
//获取elementData数组的大小,并赋值给变量oldCapacity
int oldCapacity = elementData.length;
// >> : 右移,右移几位就相当于除以2的几次幂
// << : 左移,左移几位就相当于乘以2的几次幂
//扩容的核心算法: 原容量的1.5倍
//定义扩容后的容量,并把数值赋值变量newCapacity,newCapacity为oldCapacity的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
// 若 newCapacity 依旧小于 minCapacity,则newCapacity重新赋值为minCapacity
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
//如果newCapacity 大于MAX_ARRAY_SIZE,则调用hugeCapacity重新赋值
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
//如果minCapacity小于0直接报异常
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//如果参数minCapacity大于MAX_ARRAY_SIZE,则返回Integer.MAX_VALUE,否则就直接返回MAX_ARRAY_SIZE
//从MAX_ARRAY_SIZE定义来看 Integer.MAX_VALUE 比 MAX_ARRAY_SIZE 大 8
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
//
public boolean add(E e) {
//ensureCapacityInternal方法是确保容器数组有空间,
//特别是传参为size+1,既能保证添加这个新元素数组不溢出,又不会导致空间浪费
ensureCapacityInternal(size + 1); // Increments modCount!!
//size为ArrayList中包含的元素数,
//数组是0从0下标开始的所以传进来的元素放在elementData[size]的位置
//size++表示元素e添加成功后,集合中的元素数又多了一个
elementData[size++] = e;
//如果添加成功则返回true
return true;
}
//参数为数字类型,指定的最小容量
private void ensureCapacityInternal(int minCapacity) {
//判断elementData与DEFAULTCAPACITY_EMPTY_ELEMENTDATA两个对象是否相等
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//如果相等,则取DEFAULT_CAPACITY与minCapacity当中的最大值
//DEFAULT_CAPACITY为10,minCapacity是动态的
//只有用空参构造函数创建的集合才会相等
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
//modCount是记录集合被修改的次数,这里的修改指的是增删改
//modCount是AbstractList中定义的字段
//集合修改次数加1
modCount++;
//防止溢出代码:确保指定的最小容量 > 集合的当前容量
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//minCapacity>elementData.length则说明容量不够需要扩容
grow(minCapacity);
}
//减去8 是因为Some VMs reserve some header words in an array 有些虚拟机在数组中保留一些头字
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
//获取elementData数组的大小,并赋值给变量oldCapacity
int oldCapacity = elementData.length;
// >> : 右移,右移几位就相当于除以2的几次幂
// << : 左移,左移几位就相当于乘以2的几次幂
//扩容的核心算法: 原容量的1.5倍
//定义扩容后的容量,并把数值赋值变量newCapacity,newCapacity为oldCapacity的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
// 若 newCapacity 依旧小于 minCapacity,则newCapacity重新赋值为minCapacity
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
//如果newCapacity 大于MAX_ARRAY_SIZE,则调用hugeCapacity重新赋值
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
//如果minCapacity小于0直接报异常
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//如果参数minCapacity大于MAX_ARRAY_SIZE,则返回Integer.MAX_VALUE,否则就直接返回MAX_ARRAY_SIZE
//从MAX_ARRAY_SIZE定义来看 Integer.MAX_VALUE 比 MAX_ARRAY_SIZE 大 8
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
add(E e) 方法作用实在容器最后元素的后边添加一个新元素e,注意这边指的是容器最后元素的后边,而不是指的是容器成员数组elementData的最后一个位置。add方法中的逻辑是调用ensureCapacityInternal方法,参数为集合中已有元素的个数加1即size+1。ensureCapacityInternal方法一切正常后,就会在 elementData[size] 的位置放入新元素e,至于为什么是放在size这个位置是因为数组下标是从0开始的。元素添加成功后,数组中存在的元素个数要加1即size++,而源码中把添加元素和size自增放在了一起即 elementData[size++] = e,至此元素添加成功,方法返回true。
ensureCapacityInternal方法首先会判断elementData与DEFAULTCAPACITY_EMPTY_ELEMENTDATA两个对象是否相等,如果相等则说明该ArrayList对象是通过无参构造函数创建,因为在无参构造函数中 进行了this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA操作,如果判断相等则取DEFAULT_CAPACITY与参数minCapacity当中的最大值,并把最大值赋给minCapacity,当用无参构造函数创建的对象,并第一次调用add(E e) 方法时这一步 参数minCapacity 为1,而DEFAULT_CAPACITY为10 则minCapacity会重新被赋值为10,这也就是为什么说无参构造函数会创建一个容量为10的集合。
接着ensureCapacityInternal方法会继续调用ensureExplicitCapacity,参数还是为minCapacity。
ensureExplicitCapacity方法,首先会对modCount进行一个自增,modCounts是记录集合被修改的次数,对容器数据的增删改都算对集合进行修改,modCount是AbstractList中定义的字段。modCount自增后,接种会继续判断newCapacity- elementData.length 是否大于0,minCapacity代表着容器现在所需的最小容量,而elementData.length代表着容器实际的容量,如果minCapacity大于elementData.length则说明容量不够需要扩容,反之则不需要扩容。如果不需要扩容则流程结束,如果需要扩容则调用grow()方法。
grow方法作用是扩容,获取elementData数组的大小,并赋值给新定义的变量oldCapacity,接着再定义一个int变量newCapacity,并把oldCapacity的1.5倍数赋值给newCapacity这就是说要把集合容量扩容至原来容量的1.5倍,接下来会对newCapacity与minCapacity(容器现在所需的最小容量)进行比较,如果newCapacity小于minCapacity则说明如果扩容至原来集合容量的1.5倍还是不满足实际需要,那么此时newCapacity会重新赋值为minCapacity。接下来程序会继续拿newCapacity 拿 MAX_ARRAY_SIZE进行比较,MAX_ARRAY_SIZE值为 Integer.MAX_VALUE - 8,减8是因为有些虚拟机在数组中保留一些头字。若newCapacity大于MAX_ARRAY_SIZE,则调用hugeCapacity方法获取 Integer.MAX_VALUE,并把该值重新赋值给newCapacity,至此newCapacity为真正为容器扩容后的容量大小。
接着程序会调用Arrays.copyOf()方法进行扩容。
3.2 add(int index, E element)
public void add(int index, E element) {
//检查索引是否越界
rangeCheckForAdd(index);
//如果不越界则调用ensureCapacityInternal方法
ensureCapacityInternal(size + 1); // Increments modCount!!
//将当前在该位置的元素(如果有)和 后边元素向右移动一位。
//elementData 源数组。
//index 源数组中的起始位置。
//elementData 目标数组。
//index + 1 目标数据中的起始位置。
//size - index 要复制的数组元素的数量。
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//把元素存在index的位置上
elementData[index] = element;
//集合元素个数加1
size++;
}
private void rangeCheckForAdd(int index) {
//如果index大于size或者小于0,则报异常
//index大于size也报异常,是为了让元素在容器中按照顺序依次保存数据并且避免数组下标越界
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
add(int index, E element)方法作用是在容器的指定位置插入指定元素,该方法首先会调用rangeCheckForAdd对要插入的位置进行判断是否下标越界,如果是则直接报异常,否则ensureCapacityInternal方法,ensureCapacityInternal方法与上边的一样这边就不在解释。调用ensureCapacityInternal后,就调用system.arraycopy方法把index位置的元素(如果有)和 后边元素向右移动一位。移动完成后把元素存在index的位置上,并把集合元素个数加1,至此该方法结束,该方法没有返回值
3.3 addAll(Collection extends E> c)
//将参数中的集合数据添加到ArrayList当中,添加的位置为原有元素的后边
public boolean addAll(Collection extends E> c) {
//集合转数组
Object[] a = c.toArray();
//获取数组的长度
int numNew = a.length;
//如果有需要则进行扩容,参数是集合原有元素个数加上传过来集合的元素个数
ensureCapacityInternal(size + numNew); // Increments modCount
//a 源数组。
//0 源数组中的起始位置。
//elementData 目标数组。
//size 目标数据中的起始位置。
//numNew 要复制的数组元素的数量。
//拷贝数据到ArrayList中也就是elementData数组中
System.arraycopy(a, 0, elementData, size, numNew);
//集合的元素个数进行更改,改为原来元素个数加上要加入元素的个数
size += numNew;
//numNew如果不等于零,即传过来的集合有数据则返回true
return numNew != 0;
}
addAll方法 作用是把指定集合按照Iterator返回的顺序保存在ArrayList中,保存位置为ArrayList已有元素的后边。整体流程是先把传过来的集合转成数组,然后获取数组长度并赋值给新定义的局部变量numNew,接着调用ensureCapacityInternal方法进行扩容(如果有需要),ensureCapacityInternal调用完成后进行数据拷贝,把传参数据拷贝到ArrayList中,接着修改集合元素个数即size,改为原来元素个数加上要加入元素的个数的和,接着返回numNew != 0值,如果numNew为0即传来参数为空集合则返回false,反之返回true.
3.4 addAll(int index, Collection extends E> c)
public boolean addAll(int index, Collection extends E> c) {
//校验索引
rangeCheckForAdd(index);
//集合转数组
Object[] a = c.toArray();
//获取数组的长度
int numNew = a.length;
//如果有需要则进行扩容,参数是集合原有元素个数加上传过来集合的元素个数
ensureCapacityInternal(size + numNew); // Increments modCount
//numMoved代表要移动元素的个数
int numMoved = size - index;
//numMoved大于0则说明ArrayList集合原有元素需要进行移动
if (numMoved > 0)
//对ArrayList集合元素进行移动
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
//拷贝数据到ArrayList中也就是elementData数组指定位置中
System.arraycopy(a, 0, elementData, index, numNew);
//集合的元素个数进行更改,改为原来元素个数加上要加入元素的个数
size += numNew;
//numNew如果不等于零,即传过来的集合有数据则返回true
return numNew != 0;
}
addAll(int index, Collection extends E> c)方法的作用是在已有的ArrayList集合中的指定位置添加要添加的集合元素,整体逻辑与addAll(Collection extends E> c)类似,只是多了校验索引,及对ArrayList的index及后边的元素(如果有)的移动这两步
4、删除
4.1 remove(int index)
//通过集合的指定位置删除元素,并把该位置后边的元素(如果有)前移
public E remove(int index) {
//检查坐标是否合法
rangeCheck(index);
//集合修改次数加1
modCount++;
//获取指定位置的元素
E oldValue = elementData(index);
//计算要移动的元素,减1是因为数组坐标是从0开始的
int numMoved = size - index - 1;
//如果大于0,说明要删除的元素后边还有元素
if (numMoved > 0)
//移动元素
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
//把删除的元素返回
return oldValue;
}
private void rangeCheck(int index) {
//如果index大于等于size则抛出异常
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//通过集合的指定位置删除元素,并把该位置后边的元素(如果有)前移
public E remove(int index) {
//检查坐标是否合法
rangeCheck(index);
//集合修改次数加1
modCount++;
//获取指定位置的元素
E oldValue = elementData(index);
//计算要移动的元素,减1是因为数组坐标是从0开始的
int numMoved = size - index - 1;
//如果大于0,说明要删除的元素后边还有元素
if (numMoved > 0)
//移动元素
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
//把删除的元素返回
return oldValue;
}
private void rangeCheck(int index) {
//如果index大于等于size则抛出异常
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
该方法作用是删除集合指定位置的元素,并返回删除的元素
4.2 remove(Object o)
//从此列表中删除第一次出现的指定元素(如果存在)。
//如果列表不包含该元素,则集合保持不变
public boolean remove(Object o) {
//参数是否为空
if (o == null) {
//参数为空,遍历集合进行查找
for (int index = 0; index < size; index++)
//如果找到元素为null的元素,则根据元素的下标删除元素
if (elementData[index] == null) {
//调用fastRemove删除元素
fastRemove(index);
//删除完成后返回true
return true;
}
} else {
//参数不为空,遍历集合进行查找
for (int index = 0; index < size; index++)
//如果找到指定的元素,注意此时是用equals来判读相同的,不是用==
if (o.equals(elementData[index])) {
//调用fastRemove删除元素
fastRemove(index);
//删除完成后返回true
return true;
}
}
//如果所有条件都不满足即没有找到数据并删除则返回false
return false;
}
//跳过边界检查并且不返回删除元素的私有移除方法
private void fastRemove(int index) {
//集合修改次数加1
modCount++;
//计算要移动的元素,减1是因为数组坐标是从0开始的
int numMoved = size - index - 1;
//如果大于0,说明要删除的元素后边还有元素
if (numMoved > 0)
//移动元素
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//移动完成后,把集合元素个数减1,
//并集合移动前的最后一个元素的位置置空,这样避免内存泄漏
elementData[--size] = null; // clear to let GC do its work
}
该方法作用是删除集合中第一次出现的指定元素(如果存在),即如果集合中有多个相同的元素,那么调用该方法只会删除一个元素,其他相同的元素也不会删除。如果元素被删除则返回true,否则返回false,该方法中使用随机获取的方式遍历集合而不是用顺序获取的方式遍历集合,是因为ArrayList 实现了RandmoAccess接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在ArrayList中,我们即可以通过元素的序号快速获取元素对象。这就可以提高ArrayListremove(Object o)的效率。
fastRemove方法与remove(int index)方法类似,只是少了边界检查及不用再一次根据下标查找数据并且不返回删除元素,提高效率
4.3 removeAll(Collection> c)
//从此列表中删除包含在指定集合中的所有元素
public boolean removeAll(Collection> c) {
//健壮性检查,如果参数为null,则抛出NullPointerException
Objects.requireNonNull(c);
//调用batchRemove方法删除元素
return batchRemove(c, false);
}
private boolean batchRemove(Collection> c, boolean complement) {
//新建一个局部数组变量elementData
//并把成员变量值赋给局部变量elementData
final Object[] elementData = this.elementData;
//定义int类型的局部变量r,w
int r = 0, w = 0;
//定义局部变量modified
boolean modified = false;
try {
//遍历集合
for (; r < size; r++)
//elementData[r]元素是否存传参集合中
//如果complement为true,则只有c.contains(elementData[r])为true
//即elementData[r]元素存在于传过来集合中,则进入if体,
//如果complement为fale则只有c.contains(elementData[r])为false
//即elementData[r]元素不存在于传过来集合中,则进入if体
if (c.contains(elementData[r]) == complement)
//重新对elementData进行数据存放
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
//保持与 AbstractCollection 的行为兼容性,
// even if c.contains() throws.
//即使 c.contains() 抛出
//如果r!=size则说明有异常抛出
if (r != size) {
//拷贝数组也就是r及其后边的元素,依次拷贝到w往后的位置,拷贝的元素个数为size-r
System.arraycopy(elementData, r,
elementData, w,
size - r);
//w变成w+加上size - r,其实也就是此时ArrayList集合真实存在的元素个数
w += size - r;
}
//如果w不等于size,即现在实际元素的个数与原来的元素个数不一致
if (w != size) {
// clear to let GC do its work
//从现在集合实际存在的元素个数位置开始,后边的元素都置为null,
for (int i = w; i < size; i++)
elementData[i] = null;
//集合修改次数,为原来修改次数加上调用本方法实际修改的次数
modCount += size - w;
//size 重新赋值为w即集合现在实际存在的元素个数
size = w;
//集合有修改则modified置为true
modified = true;
}
}
//返回modified
return modified;
}
removeAll(Collection> c)作用是把参数c集合中的元素从ArrayList集合中删除,该方法中核心是私有方法batchRemove,在removeAll方法中,complement为false,则c.contains(elementData[r]) 为false,才会进行数据的移动即elementData[w++] = elementData[r]。c.contains(elementData[r])为false代表着elementData[r]数据不存在与参数集合中,则此时进行elementData[w++] = elementData[r]操作会把ArrayList集合不存在于参数c集合中的元素进行重新存放,重新存放的数组中的元素都是在集合c中不存在的元素,这样就可以完成删除。
4.4 retainAll(Collection> c)
//仅保留此列表中包含在指定集合中的元素。 换句话说,从该列表中删除所有未包含在指定集合中的元素。
public boolean retainAll(Collection> c) {
//健壮性检查,如果参数为null,则抛出NullPointerException
Objects.requireNonNull(c);
//调用batchRemove方法删除不存在与c中的元素
return batchRemove(c, true);
}
retainAll仅保留此列表中包含在指定集合中的元素。 换句话说,从该列表中删除所有未包含在指定集合中的元素。与removeAll方法作用正好相反
4.6 removeIf(Predicate super E> filter)
该方法暂不解读
4.6 removeRange(int fromIndex, int toIndex)
//删除集合从fromIndex(含fromIndex)开始位置到toIndex(不含toIndex)位置的元素
protected void removeRange(int fromIndex, int toIndex) {
//集合修改次数加1
modCount++;
//size-toIndex为要移动的元素个数
int numMoved = size - toIndex;
//数据移动
//该操作就是把elementData数组从toIndex(包括toIndex)位置开始往后的元素
//移动到elementData的fromIndex及其往后的位置上,需要移动元素个数为numMoved个
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// clear to let GC do its work
//size - (toIndex-fromIndex)为此时集合应该存在的元素个数
int newSize = size - (toIndex-fromIndex);
//从newSize这个位置往后的元素都要被删除掉
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
//size 重新赋值为newSize即此时集合中元素真正的个数
size = newSize;
}
removeRange(int fromIndex, int toIndex) 方法的作用就是删除集合从fromIndex(含fromIndex)开始位置到toIndex(不含toIndex)位置的元素,图片为该删除的示意图
4.7 clear()
//从此列表中删除所有元素。此调用返回后,列表将为空。
public void clear() {
//集合修改次数加1
modCount++;
// clear to let GC do its work
//把数组中有数据的位置置为null
for (int i = 0; i < size; i++)
elementData[i] = null;
//此时集合元素个数为0
size = 0;
}
5、修改
5.1 set(int index, E element)
//用指定的元素替换此列表中指定位置的元素
public E set(int index, E element) {
//校验索引
rangeCheck(index);
//根据索引取出元素 --> 被替换的元素
E oldValue = elementData(index);
//把element存入到elementData数组中
elementData[index] = element;
//返回被替换的元素
return oldValue;
}
6、查询
6.1 get(int index)
//返回此列表中指定位置的元素
public E get(int index) {
//校验索引
rangeCheck(index);
//调用elementData()方法返回该位置的元素
return elementData(index);
}
// Positional Access Operations 位置访问操作
@SuppressWarnings("unchecked")
E elementData(int index) {
//返回elementData数组index位置的元素
return (E) elementData[index];
}
6.2 indexOf(Object o)
//返回此列表中指定元素第一次出现的索引,如果此列表不包含该元素,则返回 -1
//查找顺序是从0位置开始依次加1
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
//如果没有找到则返回-1
return -1;
}
6.3 lastIndexOf(Object o)
//返回此列表中指定元素最后一次出现的索引,如果此列表不包含该元素,则返回 -1
//查找顺序是从数组最后一个元素位置开始,依次减1
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
//如果没有找到则返回-1
return -1;
}
6.4 contains(Object o)
//如果此列表包含指定的元素,则返回true
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
7、其他
7.1 trimToSize()
//将此ArrayList实例的容量修剪为列表的当前大小。
// 应用程序可以使用此操作来最小化ArrayList实例的存储空间。
public void trimToSize() {
//集合修改次数加1
modCount++;
//判断集合容量是否大于集合元素个数
if (size < elementData.length) {
//如果size等于0即此时集合没有元素则elementData赋值为EMPTY_ELEMENTDATA
//否则重新创建一个数组,大小为size并把元素移到新数组中,并赋值给elementData
elementData = (size == 0)
? EMPTY_ELEMENTdata: Arrays.copyOf(elementData, size);
}
}
7.2 ensureCapacity(int minCapacity)
//增加集合容量,
public void ensureCapacity(int minCapacity) {
//elementData如果等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA则minExpand赋值为0
//否则minExpand为DEFAULT_CAPACITY即为10
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
//如果minCapacity大于minExpand则进行扩容(如果有需要)
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
//返回此列表中指定位置的元素
public E get(int index) {
//校验索引
rangeCheck(index);
//调用elementData()方法返回该位置的元素
return elementData(index);
}
// Positional Access Operations 位置访问操作
@SuppressWarnings("unchecked")
E elementData(int index) {
//返回elementData数组index位置的元素
return (E) elementData[index];
}
6.2 indexOf(Object o)
//返回此列表中指定元素第一次出现的索引,如果此列表不包含该元素,则返回 -1
//查找顺序是从0位置开始依次加1
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
//如果没有找到则返回-1
return -1;
}
6.3 lastIndexOf(Object o)
//返回此列表中指定元素最后一次出现的索引,如果此列表不包含该元素,则返回 -1
//查找顺序是从数组最后一个元素位置开始,依次减1
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
//如果没有找到则返回-1
return -1;
}
6.4 contains(Object o)
//如果此列表包含指定的元素,则返回true
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
7、其他
7.1 trimToSize()
//将此ArrayList实例的容量修剪为列表的当前大小。
// 应用程序可以使用此操作来最小化ArrayList实例的存储空间。
public void trimToSize() {
//集合修改次数加1
modCount++;
//判断集合容量是否大于集合元素个数
if (size < elementData.length) {
//如果size等于0即此时集合没有元素则elementData赋值为EMPTY_ELEMENTDATA
//否则重新创建一个数组,大小为size并把元素移到新数组中,并赋值给elementData
elementData = (size == 0)
? EMPTY_ELEMENTdata: Arrays.copyOf(elementData, size);
}
}
7.2 ensureCapacity(int minCapacity)
//增加集合容量,
public void ensureCapacity(int minCapacity) {
//elementData如果等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA则minExpand赋值为0
//否则minExpand为DEFAULT_CAPACITY即为10
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
//如果minCapacity大于minExpand则进行扩容(如果有需要)
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
//返回此列表中指定元素最后一次出现的索引,如果此列表不包含该元素,则返回 -1
//查找顺序是从数组最后一个元素位置开始,依次减1
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
//如果没有找到则返回-1
return -1;
}
6.4 contains(Object o)
//如果此列表包含指定的元素,则返回true
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
7、其他
7.1 trimToSize()
//将此ArrayList实例的容量修剪为列表的当前大小。
// 应用程序可以使用此操作来最小化ArrayList实例的存储空间。
public void trimToSize() {
//集合修改次数加1
modCount++;
//判断集合容量是否大于集合元素个数
if (size < elementData.length) {
//如果size等于0即此时集合没有元素则elementData赋值为EMPTY_ELEMENTDATA
//否则重新创建一个数组,大小为size并把元素移到新数组中,并赋值给elementData
elementData = (size == 0)
? EMPTY_ELEMENTdata: Arrays.copyOf(elementData, size);
}
}
7.2 ensureCapacity(int minCapacity)
//增加集合容量,
public void ensureCapacity(int minCapacity) {
//elementData如果等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA则minExpand赋值为0
//否则minExpand为DEFAULT_CAPACITY即为10
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
//如果minCapacity大于minExpand则进行扩容(如果有需要)
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
7.1 trimToSize()
//将此ArrayList实例的容量修剪为列表的当前大小。
// 应用程序可以使用此操作来最小化ArrayList实例的存储空间。
public void trimToSize() {
//集合修改次数加1
modCount++;
//判断集合容量是否大于集合元素个数
if (size < elementData.length) {
//如果size等于0即此时集合没有元素则elementData赋值为EMPTY_ELEMENTDATA
//否则重新创建一个数组,大小为size并把元素移到新数组中,并赋值给elementData
elementData = (size == 0)
? EMPTY_ELEMENTdata: Arrays.copyOf(elementData, size);
}
}
7.2 ensureCapacity(int minCapacity)
//增加集合容量,
public void ensureCapacity(int minCapacity) {
//elementData如果等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA则minExpand赋值为0
//否则minExpand为DEFAULT_CAPACITY即为10
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
//如果minCapacity大于minExpand则进行扩容(如果有需要)
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
//增加集合容量,
public void ensureCapacity(int minCapacity) {
//elementData如果等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA则minExpand赋值为0
//否则minExpand为DEFAULT_CAPACITY即为10
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
//如果minCapacity大于minExpand则进行扩容(如果有需要)
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
ensureCapacity是为了对集合进行扩容,但这个扩容参数如果小于现有容器的容量则不会扩容
7.3 size()
//获取集合元素个数
public int size() {
return size;
}
7.4 isEmpty()
//判断元素个数是否为0
public boolean isEmpty() {
return size == 0;
}
7.5 clone()
//对集合进行浅拷贝,对应的数据不会被复制所以说只是一个浅拷贝
public Object clone() {
try {
ArrayList> v = (ArrayList>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
//集合修改次数位置0
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
7.6 toArray()
//集合转成数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
//判断元素个数是否为0
public boolean isEmpty() {
return size == 0;
}
7.5 clone()
//对集合进行浅拷贝,对应的数据不会被复制所以说只是一个浅拷贝
public Object clone() {
try {
ArrayList> v = (ArrayList>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
//集合修改次数位置0
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
7.6 toArray()
//集合转成数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
//集合转成数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
此时转成的数组其class类型 仍然为Object[]类型
7.7 toArray(T[] a)
@SuppressWarnings("unchecked")
//集合转成 指定的运行时类型的数组
public T[] toArray(T[] a) {
//如果参数数组长度小于集合的元素个数
//即传过来的数组放不下集合的所有元素
//则新建一个长度为集合元素的个数的数组,数组类型与参数类型一致
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
//创建新数组并把集合数据依次存在数组中并返回
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
//如果参数数组长度大于等于集合元素个数则直接把集合中的元素复制到数组中
System.arraycopy(elementData, 0, a, 0, size);
//如果参数数组长度大于元素个数
if (a.length > size)
//则数组的size位置置为空,至于为什么这么做,应该是为了确定数组中哪些数据是从集合中复制过来的
//但是这么做,有一个缺陷就是集合中本身没有null元素,否则就没什么意义
a[size] = null;
//返回数组
return a;
}
7.8 iterator()
public Iterator iterator() {
return new Itr();
}
获取集合的迭代器
总结
以上就是对ArrayList的大部分代码的走读



