尝试分析ArrayList源码。
首先看一下他的 类图 的结构
可序列化、可克隆、可迭代、可随机访问。
可随机访问这个接口,就是说for循环遍历集合会优于迭代器遍历。
间接继承了collection 和 list 接口,一般常用 list 接口的方法。常用 list 通过多态引用 ArrayList 对象。
上图就是说 ArrayList 中有上方直接或间接父类、接口中的功能的集合体
来看 ArrayList 中的部分参数,源码:
//默认的初始容量
private static final int DEFAULT_CAPACITY = 10;
//一个空数组
private static final Object[] EMPTY_ELEMENTDATA = new Object[0];
//空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];
//序列化时被忽略,ArrayList 集合底层用于 保存数据 的 对象数组
transient Object[] elementData;
//有效元素个数
private int size;
//数组容量最大值 int 最大值(2147483647) - 8
private static final int MAX_ARRAY_SIZE = 2147483639;
关于最大容量为什么少 8 个容量,
some VMs reserve some header words in a array
某些虚拟机会在一个数组中保存一些头信息
这是作者说的,如果尝试定义大数组时可能造成内存溢出错误 (OutOfMemoryError),
所以最大容量给你限制了 8 个。但我实际测试的时候可以创建的大小很小,要远小于这个最大容量,要不然heap空间不够,可以通过设置来增加 虚拟机的内存。
接着往下看构造函数,源码:
//无参构造函数
public ArrayList() {
//直接将底层数组初始化为一个空的数组,即:你不用集合,我就不站你空间!
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//自定义初始化容量
public ArrayList(int initialCapacity) {
//如果初始化的容量大于0
if (initialCapacity > 0) {
//就创建你指定大小的对象数组
this.elementData = new Object[initialCapacity];
} else {
//如果初始化容量小于 0,抛异常:不合法的容量大小
if (initialCapacity != 0) {
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
}
//最后一种情况就是初始化的容量是 0 ,就直接把创建好的空数组引用给保存数据的数组
this.elementData = EMPTY_ELEMENTDATA;
}
}
//根据一个 Collection接口 体系内的集合来初始化一个数组。
public ArrayList(Collection<? extends E> c) {
//调用初始化数组的,toArray 方法将其直接转换成数组对象
this.elementData = c.toArray();//Collection 实现类中,会有这个方法的实现
//将数组对象的长度付给成员变量 size (元素个数计数器)
if ((this.size = this.elementData.length) != 0) {//如果元素个数不是 0
//这里判断了一下底层数组的类型是不是 Object 对象数组
if (this.elementData.getClass() != Object[].class) {
//如果不是的话就将其保存在一个Object对象数组中
this.elementData = Arrays.copyOf(this.elementData, this.size, Object[].class);
}
} else {
//如果传入的集合的元素个数就是 0,那么就直接把定义好的空数组交给底层对象数组
this.elementData = EMPTY_ELEMENTDATA;
}
}
单独解释一下上面的这个 if 判断
if (this.elementData.getClass() != Object[].class)
这个 If 判断,当之前的 toArray 方法调用后,有可能返回的不是一个Object[] 引用,可能是比如: String[] 或者某个类行的数组,即:
String[] s = new String[1];
Object[] o = s;
上面的 o 不是 Object[] 类型的引用。
所以避免这种情况,产生了这个判断,来确定一下底层数组的引用类型为 Object[] ,如果不是这个类型就将原数组中的数据,保存在 Object[] 类型的数组中。这里只知道他是这么处理的,至于为什么要这么处理,这么处理之后会对以后的代码产生什么影响我就不知道了。。。要是有大神明白,恳请提点!!!
接下来就是常用方法了
add(E e) 源码:
public boolean add(E e) {
//一个计数操作,记录对集合修改的次数,在迭代器中作为校验
++this.modCount;
//调用真正的添加方法,参数:1.要添加的对象 2.底层数据对象数组 3.当前数组元素个数
this.add(e, this.elementData, this.size);
return true;
}
private void add(E e, Object[] elementData, int s) {
/*
首先要判断一下数组大小是否可以再容纳一个数据
如果当前数组中有效元素个数 s 已经跟数组的容量大小一致时,
即,底层数组已经没有空间容纳将要保存的数据 e 了
此时需要扩容(扩容方法 grow 接下来单独解析)。
*/
if (s == elementData.length) {
elementData = this.grow();//扩容方法
}
//到这里时 数组的容量是足够添加一个新数据的,那么就将数据添加到数组当中
//位置的索引是有效元素 s 的值,意味着 索引位 0 ~ s-1 的元素都是有效元素
elementData[s] = e;
//添加完元素之后,让有效元素的个数加一
this.size = s + 1;
}
grow() 方法
//add调这个方法
private Object[] grow() {
/*
将 有效元素个数+1 传入到 grow(int) 方法中,
这个方法接收的参数是最小容量 minCapacity,
这里 +1 意味着扩容至少要扩容一个,避免 size 是 0 的情况,扩容了 0 这种情况,
也可以这样理解:我现在要添加一个数据,那么你容量不够用了,要扩容的话至少要扩容一个保证我要添加的这个数据能插入进去
*/
return this.grow(this.size + 1);
}
//这个方法来真正进行扩容
private Object[] grow(int minCapacity) {
//调用了两个方法:Arrays.copyOf 和 newCapacity
//Arrays.copyOf 是用来将原数组复制到新建的数组中,这个新建的数组大小是通过 newCapacity 方法获得的
//newCapacity 根据最小容量来计算扩容后的数组容量
return this.elementData = Arrays.copyOf(this.elementData, this.newCapacity(minCapacity));
}
//这个方法根据一个最小容量来获取扩容大小
private int newCapacity(int minCapacity) {
//获取未扩容的数组容量大小
int oldCapacity = this.elementData.length;
//新数组的容量是老数组的容量的 1.5倍 即 old + (old/2) 右移一位相当于 除2 取整
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果 新容量 - 最小容量 不为正数
if (newCapacity - minCapacity <= 0) {
//判断当前的数组是否是没使用过的数组,这里就使用两个 空数组加以区分,一个是调用无参构造时elementData引用的默认的空数组,一个是初始化传的容量值为 0 时elementData引用的空数组
if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//从传进来的最小容量 和 10 之间选择一个最大的,作为新容量返回,相当于第一次添加数据的时候,才为数组定义大小(最小就是 10 )
return Math.max(10, minCapacity);
} else if (minCapacity < 0) {
//如果最小容量小于0 就抛异常,内存溢出
throw new OutOfMemoryError();
} else {
//如果以上情况都不是,那么至少要返回的新容量足以添加当前需要插入的数据,
//至少容量扩容一个,足以让我把当前的数据添加进集合
return minCapacity;
}
} else {
//如果计算的得到的 新容量-最小容量 > 0
/*
三目运算符:
判断 新容量 <= 默认的数组最大容量(MAX_ARRAY_SIZE)
直接返回这个计算得到的 newCapacity
新容量 > 默认的数组对大容量
调用 hugeCapacity 方法
*/
return newCapacity - 2147483639 <= 0 ? newCapacity : hugeCapacity(minCapacity);
}
}
//当 新容量 大于 MAX_ARRAY_SIZE 调用此函数
private static int hugeCapacity(int minCapacity) {
//最小容量小于0,抛异常
if (minCapacity < 0) {
throw new OutOfMemoryError();
} else {
//当最小值是大于 MAX_ARRAY_SIZE 的时候 返回 MAX_ARRAY_SIZE + 8
//否则返回 MAX_ARRAY_SIZE
return minCapacity > 2147483639 ? 2147483647 : 2147483639;
}
}