源码分析ArrayList
先初始化一个ArrayList,是我创建了一个student集合
List<Student> list = new ArrayList<>();
1、我们进入ArrayList类看看
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
可以看到ArrayList继承了AbstractList抽象List,实现了List、RandomAccess, Cloneable, java.io.Serializable接口
可以说ArrayList天赋异禀
List:做为一个集合的所有方法,
RandomAccess:实现使用的标记接口,用于指示它们支持快速(通常为固定时间)随机访问。此接口的主要目的是允许通用算法在应用于随机或顺序访问列表时改变其行为,以提供良好的性能。
Cloneable:可以复制
Serializable:可以序列化
2、看看他的静态变量
private static final long serialVersionUID = 8683452581122892189L;
//指定ArrayList的默认容量为10
private static final int DEFAULT_CAPACITY = 10;
//用于空数组实例
private static final Object[] EMPTY_ELEMENTDATA = {};
//用于默认大小的空数组实例
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//指明ArrayList的当前存储数据
transient Object[] elementData; // non-private to simplify nested class access
//指明ArrayList的当前存储大小
private int size;
3、看初始化构造
ArrayList有三种构造方式
/**
* 1、输入单一参数,容量
* 直接指定容量,如果指定的容量为0则将内容置空,如果小于0则报错误初始容量
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* 2、空参构造,内容置空
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 3、参数为集合
* 先将集合的内容转为数组,再创建一个ArrayList将原来的内容复制进去
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// 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、主要方法
size(),返回数组当前大小,返回int类型
public int size() {
return size;
}
isEmpty(),判断是否为空,如果当前数组大小为0则返回true,反之false
public boolean isEmpty() {
return size == 0;
}
contains(Object o),判断是否包含某对象,包含返回true,反之false
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
indexOf(Object o),返回传入对象的当前索引值,可以传null,如果传入对象不存在的话,返回-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;
}
return -1;
}
get(int index),通过索引获取对应对象,如果索引超出当前数组大小,报错索引越界
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
add(E e),添加一个元素,没啥好说的
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
remove(Object o),从数组中移除传入对象
private void fastRemove(int index) {
modCount++;
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
}
这个方法面试经常会被问到。(ArrayList如何删除对象?ArrayList删除的原理是什么?ArrayList删除为什么不能使用for循环?等等)
这里展示的是remove(Object o)所调用的删除逻辑,我们仔细分析一下。
modCount表示被修改的次数,当调用remove的时候,这个值自增
numMoved = 当前数组大小 - 移除对象索引 - 1 = 移除第numMoved个对象,相当于校验索引
先将数组前面不变的部分复制过来,再将从被移除对象后面的对象开始整体前移一个单位
最后,将数组大小减1。
问题就出现在这里,如果使用for循环进行remove,则数组的大小不断减1,而循环的次数缺没有发生改变,因此会报错
所以准确的删除应该是使用迭代器删除
我后期会写一个更为详细的表达