ArrayList是基于动态数组实现增删改查操作,进行顺序存储的,在内存中各个元素的地
址是连续的.
ArrayList组织结构图:
根据上面的结构图知道ArrayList继承AbstractList并实现了RandomAccess,Cloneable,Serializable这三个标记接口,所以AbstractList具有如下特性:
- AbstractList:实现了增删改查的集合操作,并可以对集合进行迭代查询。
- RandomAccess:随机访问性能很高.
- Cloneable:ArrayList对象可以进行克隆.
- Serializable:ArrayList对象可以进行序列化和反序列化存储和读取.
ArrayList简要基本用法:
public boolean add(E var1)//在集合末尾添加数据var1
public void add(int index, E var2)//在索引位置为index的位置处插入数据var2
public E remove(int index)//删除集合中索引位置为index的元素.
public boolean remove(Object var1)//删除集合中值为var1的元素,查找元素var1是用var1的equals方法比对的,如果var1是null,就查找集合中值为null的元素并删除.(这里需要注意的是删除的元素是集合中第一个为var1或null的元素,并不是所有).
public E set(int index, E var2)//修改索引为index元素的值为var2;
public E get(int index)//获取索引index处的元素数据.
ArrayList的遍历:
增强式for循环
ArrayList<Integer> arrayList=new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
for (Integer integer:arrayList){
System.out.println(integer);
}
Iterator迭代
ArrayList<Integer> arrayList=new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
Iterator<Integer> iterator=arrayList.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
ArrayList的元素过滤
Iterator迭代过滤
ArrayList<Integer> arrayList=new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
Iterator<Integer> iterator=arrayList.iterator();
while (iterator.hasNext()){
Integer integer=iterator.next();
if(integer>=3){//删除ArrayList集合中大于等于3的所有元素
iterator.remove();
}
}
for循环过滤
抛出java.util.ConcurrentModificationException异常,所以这种方式是错误的,后面会介绍原因.
ArrayList<Integer> arrayList=new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
for(Integer e:arrayList){
if(e>=3){
arrayList.remove(e);
}
}
ArrayList源码解析
成员变量说明:
private static final int DEFAULT_CAPACITY = 10默认存储空间为10
private static final Object[] EMPTY_ELEMENTDATA = new Object[0]空动态数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];空容量数组
transient Object[] elementData;动态获取存储地址的数组elementData,它是不可以进行序列化的,作用与static修饰符一样.
private int size;数组中有效元素的个数。
private static final int MAX_ARRAY_SIZE = 2147483639;//动态数组的最大容量.
构造函数说明:
默认构造函数,创建一个空容量的数组给elementData
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
创建指定容量的数组集合。
public ArrayList(int var1) {
if(var1 > 0) {//大于0创建指定长度的数组给elementData
this.elementData = new Object[var1];
} else {
if(var1 != 0) {
throw new IllegalArgumentException("Illegal Capacity: " + var1);//如果小于0抛出异常
}
this.elementData = EMPTY_ELEMENTDATA;//若等于0赋值空数组.
}
}
创建一个初始数据为var1的集合给elementData;
public ArrayList(Collection<? extends E> var1) {
this.elementData = var1.toArray();//首先调用集合的toArray转换为数组并赋值给elementData
if((this.size = this.elementData.length) != 0) {
if(this.elementData.getClass() != Object[].class) {
this.elementData = Arrays.copyOf(this.elementData, this.size, Object[].class);
}
} else {
this.elementData = EMPTY_ELEMENTDATA;//长度为0,赋值空数组
}
}
公共方法解析:
add方法源码:
public boolean add(E var1) {
this.ensureCapacityInternal(this.size + 1);
this.elementData[this.size++] = var1;
return true;
}
1.它首先调用ensureCapacityInternal确保数组元素的空间足够, ensureCapacityInternal源码如下:
private void ensureCapacityInternal(int var1) {
if(this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
var1 = Math.max(10, var1);
}
this.ensureExplicitCapacity(var1);
}
2.判断数组是不是空的,如果是空的,则首次为它分配的容量至少为10,然后调用ensureExplicitCapacity.
private void ensureExplicitCapacity(int var1) {
++this.modCount;
if(var1 - this.elementData.length > 0) {
this.grow(var1);
}
}
3.modCount表示内部修改次数(后面会解释),判断需要的长度是否大于当前数组elementData的长度,如果是就执行grow方法进行扩容.
private void grow(int var1) {
int var2 = this.elementData.length;
int var3 = var2 + (var2 >> 1);//右移一位相当于除2,所以是1.5倍
if(var3 - var1 < 0) {
var3 = var1;
}
if(var3 - 2147483639 > 0) {
var3 = hugeCapacity(var1);
}
this.elementData = Arrays.copyOf(this.elementData, var3);
}
4.首先将当前数组的长度扩容为1.5倍,如果此时扩容后的容量仍然小于需要的长度,就将当前数组的长度改为需要的长度.
public void add(int index, E var2) {
this.rangeCheckForAdd(var1);
this.ensureCapacityInternal(this.size + 1);
System.arraycopy(this.elementData, var1, this.elementData, var1 + 1, this.size - var1);
this.elementData[var1] = var2;
++this.size;
}
这个方法与add(E var1)差不多,实现的思想是首先判断索引是否越界,如果越界抛出IndexOutOfBoundsException异常,否则执行确保容量足够的方法,其次将index索引后的所有元素向后移动, z在index索引处插入元素var1,最后修改size+1;
remove操作
public E remove(int var1) {
this.rangeCheck(var1);
++this.modCount;
Object var2 = this.elementData(var1);
int var3 = this.size - var1 - 1;
if(var3 > 0) {
System.arraycopy(this.elementData, var1 + 1, this.elementData, var1, var3);
}
this.elementData[--this.size] = null;
return var2;
}
首先检查是否数组越界,如果没有越界执行modCount+1,修改modCound的值,然后获得数组elemntData中索引为var1的元素,计算index数组复制的便宜量,即将index+1处的所有元素向前移动,最后修改数组末尾的元素值为null,等待GC回收并返回删除的元素.
public boolean remove(Object var1) {
int var2;
if(var1 == null) {
for(var2 = 0; var2 < this.size; ++var2) {
if(this.elementData[var2] == null) {
this.fastRemove(var2);
return true;
}
}
} else {
for(var2 = 0; var2 < this.size; ++var2) {
if(var1.equals(this.elementData[var2])) {
this.fastRemove(var2);
return true;
}
}
}
return false;
}
private void fastRemove(int var1) {
++this.modCount;
int var2 = this.size - var1 - 1;
if(var2 > 0) {
System.arraycopy(this.elementData, var1 + 1, this.elementData, var1, var2);
}
this.elementData[--this.size] = null;
}
remove(Object var1)删除数组元素需要区分null和非null值,非null值比较的对象是用equals.上面源码比较简单不再重复赘述.
清空ArrayList集合元素
public void clear() {
++this.modCount;
for(int var1 = 0; var1 < this.size; ++var1) {
this.elementData[var1] = null;
}
this.size = 0;
}
实现思想是:迭代数组中的元素分别设置为null,修改size为0,modCount++;
ArrayList实现了Iterable接口
public interface Iterable<T> {
Iterator<T> iterator();
}
子类需要实现iterator()方法并返回Iterator对象
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
调用ArrayList的iterator()方法调用下面的逻辑实现:
public Iterator<E> iterator() {
return new ArrayList.Itr();
}
private class Itr implements Iterator<E> {
int cursor;
int lastRet;
int expectedModCount;
private Itr() {
this.lastRet = -1;
this.expectedModCount = ArrayList.this.modCount;
}
public boolean hasNext() {
return this.cursor != ArrayList.this.size;
}
public E next() {
this.checkForComodification();
int var1 = this.cursor;
if(var1 >= ArrayList.this.size) {
throw new NoSuchElementException();
} else {
Object[] var2 = ArrayList.this.elementData;
if(var1 >= var2.length) {
throw new ConcurrentModificationException();
} else {
this.cursor = var1 + 1;
return var2[this.lastRet = var1];
}
}
}
public void remove() {
if(this.lastRet < 0) {
throw new IllegalStateException();
} else {
this.checkForComodification();
try {
ArrayList.this.remove(this.lastRet);
this.cursor = this.lastRet;
this.lastRet = -1;
this.expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException var2) {
throw new ConcurrentModificationException();
}
}
}
实际上得到的是内部类Itr的实例,注意上面执行next()方法每次迭代器操作的时候都要首先调用checkForComodification(),它是什么呢?
final void checkForComodification() {
if(ArrayList.this.modCount != this.expectedModCount) {
throw new ConcurrentModificationException();
}
}
看到了吧它就是用来比对modCount与expectModCount的值,这里就知道了为什么对集合进行删除,添加的时候需要修改modCount的值,当二者不等的时候抛出异常.
iterator执行remove()方法时会执行this.expectedModCount = ArrayList.this.modCount;二者永远相等所以进行迭代执行过滤操作的时候不会出现异常。而for循环是发生了并发修改异常,为什么呢?迭代器内部会维护一些索引位置相关的数据,要求在迭代过程中,容器不能发生结构性变化,否则这些索引位置就失效了。所谓结构性变化就是添加、插入和删除元素,只是修改元素内容不算结构性变化。
ArrayList的特点:
- 查询效率高
- 删除和添加元素需要移动数组所以效率比较低
- 与LinkList正好相反,后面会分析LinkList的实现源码。