Collection接口
1.继承关系
2.实现类及方法
2.1 ArrayList
ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
,是 List 的一个实现类。
和初始化相关的一些变量
transient Object[] elementData;
private static final int DEFAULT_CAPACITY = 10//扩容时会使用
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//初始化时首先会创建一个长度为0的数组,使用该静态变量赋值,为什么要再声明一个一样的空数组呢?在下面分析中看
初始化
public ArrayList() {//空参构造函数默认创建一个空的数组,类似于懒汉式,不像jdk1.7以前初始就是长度为10
//注意这里用到赋值的常量是DEFAULTCAPACITY_EMPTY_ELEMENTDATA
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {//传入参数大于零时建立参数大小的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
/*初始化长度为0的数组,和上面的空参初始化相比,虽然建的都是空数组,但是
注意这里用到的常量是EMPTY_ELEMENTDATA*/
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList(Collection<? extends E> c) {//根据已有集合初始化
elementData = c.toArray();
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
//因为默认的elementData数组时Object类型,当参数集合类型不是Object类型时,转换为Object类型
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {//如果参数中集合元素为空就创建一个空数组
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
添加元素
添加单个元素
public boolean add(E e) {//添加单个元素
ensureCapacityInternal(size + 1);//对 elementData 根据不同情况进行扩容
elementData[size++] = e;//在在扩容后的数组elementData末尾添加元素e
return true;//添加成功,如果这里不成功了,可能就是上面 OOM 了
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));//扩容
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//数组 elementData 长度每次变化这个值都会 +1
/*如果添加元素之后需要的数组实际长度,也就是传进来的参数 minCapacity 比原先的数组长度大————也就是
数组需要扩容了,这个时候 grow*/
if (minCapacity - elementData.length > 0)/*这时候数组 elementData 长度至少是 10 了,
如果小于 10 时 calculateCapacity(...) 肯定返回的是 10,一定比数组长度 10 长 */
grow(minCapacity);//实际长度超过 10 这个默认长度时进行扩容
}
private void grow(int minCapacity) {//扩容函数
int oldCapacity = elementData.length;//现在数组长度
int newCapacity = oldCapacity + (oldCapacity >> 1);//按照1.5倍扩展数组
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;//扩容之后还不够不再进行1.5了,直接填满,正好就行
if (newCapacity - MAX_ARRAY_SIZE > 0)/*MAX_ARRAY_SIZE ArrayList 最大长度
超过这个长度造成 OOM*/
newCapacity = hugeCapacity(minCapacity);//最高返回Integer.MAX_VALUE
elementData = Arrays.copyOf(elementData, newCapacity);/*对数组 elementData 进行扩容,
多余没有赋值对的部分为默认值*/
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {/*计算添加一个元素
后数组应该具有的长度*/
/*如果当初初始化时使用的时空参构造器,那么初始的 elementData 就是指向常量空数组
DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的,这种情况下返回的就是 DEFAULT_CAPACITY
== 10 和添加元素后实际长度 minCapcity 中较大的数————也就是说如果初始化一个空的数组,
那么当添加元素的时候,数组会按照至少长度 10 来扩容*/
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
//如果初始化不是使用空参构造器创建的空数组,那么直接返回需要的实际长度作为数组 elementData 的长度
return minCapacity;
}
检验某个元素是否包含在列表中
public boolean contains(Object o) {/*遍历过程是通过列表元素的 equals() 方法判断的,是“内容”判断,
不是地址判等————所以添加到ArrayList中的元素一定要重写 equals 方法,否则就会判定地址,这样除了 null 元素
之外别的元素就算存在也会因为地址不同找不到了*/
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {//元素指向 null ,搜索列表中 null,就不用调用equals了
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {//其余情况下,调用列表元素重写的 equals() 函数
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
同理,ArrayList中所有涉及到搜索比较的 remove(…),indexOf(…)等都会调用列表元素的 equals() 方法,一定记得添加的元素要重写该方法