全部是个人探讨源码的个人理解,如果有错误,麻烦一定要在评论区指正呀,给孩子改正的机会。
欢迎评论区讨论不同的看法。
目录
ArrayList(int initialCapacity)有参构造方法:传入一个initialCapacity
ArrayList(Collection c)有参构造方法:传入一个集合
addAll(int index, Collection c)方法:
实现接口:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{}
1、Serializable(序列化)
2、Cloneable(拷贝)浅拷贝和深拷贝
3、RandomAccess(表示其随机访问(get方式)的效率高于顺序访问(迭代器))
RandomAccess接口在应用场景中,如果查询时是一个海量的集合,可以判断是否实现了RandomAccess接口,如果实现了,优先使用随机访问,否则使用顺序访问。
4、List
ArrayList:存储的是元素的内存地址,内存地址指向堆中的对象。
变量:
private static final long serialVersionUID = 8683452581122892189L;//序列化id
//默认容量
private static final int DEFAULT_CAPACITY = 10;
//空的数组容器
private static final Object[] EMPTY_ELEMENTDATA = {};
//默认的空数组容器
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
数组容器实例变量
transient Object[] elementData;
数组容器的大小(不是数组的长度!!)
private int size;
方法:
平时用的比较多的优先解释。
ArrayList无参构造方法:
会把默认容量为空的数组赋值给当前数组容器,这样在添加元素时会判断是不是该数组,如果是则调用grow()创建一个长度为10的数组。如果不是,代表本身数组容器已经被创建出来了。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
minCapacity(数组当前元素个数+1)
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//判断是不是默认为空的数组
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
ArrayList(int initialCapacity)有参构造方法:传入一个initialCapacity
如果大于0,会对应创建相应数组容量的数组(可以小于10),如果为0,则是赋值一个空数组EMPTY_ELEMENTDATA(静态变量,与前面的默认容量为空的数组不是同一个对象)
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);
}
}
ArrayList(Collection<? extends E> c)有参构造方法:传入一个集合
调用集合toArray(),判断集合是否为空,如果为空,把EMPTY_ELEMENTDATA复制给当前容器数组,如果不为空,则继续判断,数组类型是否是Object类型,如过不是则拷贝为Object类型的数组。
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();//将集合转化为数组
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA;
}
}
grow():
完成数组的拷贝,还有无参构造时创建长度为10的数组长度
private void grow(int minCapacity) {//传入当前容量
int oldCapacity = elementData.length;//当前数组长度
int newCapacity = oldCapacity + (oldCapacity >> 1);//数组长度扩大
if (newCapacity - minCapacity < 0)//扩大后的长度小于传入的长度,就给予传入的长度
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)//判断长度是否大于最大值
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);//数组拷贝
}
get():
获取对应数组下标到元素
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
set():
简单到数组赋值,返回原来元素
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
add(E e)方法:
如果是顺序添加元素,则可以优化数组插入数据的缺点,不会涉及到数组的拷贝
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;//赋值对应的下标
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//集合修改的次数,这个是继承父类AbstractList的实例变量
if (minCapacity - elementData.length > 0)
grow(minCapacity);//如果加入元素后的容量不足,扩容原来的数组
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//如果是默认为空的数组,则把默认为10的容量和minCapacity比较大小,因为后面
//添加一个集合的时候,容量可能会超过10,这样就创建minCapacity长度的数组
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
添加第一个元素后,如果是无参初始化,则会创建一个容量为10的数组来存储元素,如果继续添加元素,则会判断元素容量是否大于容器数组长度:
如果大于进行数组拷贝grow(),如果当前数组长度为偶数,拷贝为原来数组的1.5倍,如果为奇数,则不会是1.5倍,因为扩容的数组长度是原数组长度右移一位。
如果小于,则是单独的添加元素到数组中。
扩容的倍速原理代码:
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
add(int index, E element)方法:
对应下标插入元素,会涉及到数组的拷贝,不推荐使用,性能很差。
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1);
System.arraycopy(elementData, index, elementData, index + 1,
size - index);//插入数组的元素后面的后移
elementData[index] = element;
size++;
}
remove(int index)方法:删除对应下标的元素
public E remove(int index) {
rangeCheck(index);//判断是否越界,如果越界抛异常
modCount++;//修改次数+1
E oldValue = elementData(index);/取出对应下标要删除的元素
int numMoved = size - index - 1;//对应下标后元素的个数
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//数组拷贝,把删除元素后的元素拷贝到新的数组前一个位置
//arraycopy(原数组,原数组起始位置,目标数组,目标起始位置,复制元素的个数)
elementData[--size] = null; // 让GC清理
return oldValue;//返回对应下标要删除的元素
}
remove(Object o)方法:
删除数组中第一个对象,遍历数组,先找到第一个该对象的下标,如果找不到就返回false,找到再进行删除,fastRemove和remove差不多
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {//真正删除是此方法,和remove(int index)差不多
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null;
}
clean方法:
保存数组长度,让gc清理里面的元素,因为如果堆中的对象没有引用,则gc会清理对象。
public void clear() {
modCount++;
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
addAll(Collection c)方法:
将传入的集合转化为数组a,判断添加a数组的长度加上元素的个数是否大于原数组长度elementData.length,如果大于进行扩容,如果扩容后仍不足以存储,则扩容为size+numNew,然后将a数组复制到原数组elementData上,从size开始,复制numNew个元素,然后修改elementData的元素个数,方法返回ture的话说明,插入成功,如果返回false,说明添加的集合元素为0。
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew);
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
addAll(int index, Collection<? extends E> c)方法:
配合addAll(Collection c)一起理解,改动一些。
public boolean addAll(int index, Collection<? extends E> c) {
//检查下标是否在0到size,不是则抛异常,这里可以等于size,因为进行add就是
//在数组后或者前添加元素,区别rangeCheck(index),后者是不可以等于size。
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew);
int numMoved = size - index;//需要移动到元素
//判断移动到元素大于0到话,先把需要移动到元素拷贝到新数组后
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
//这说明不需要移动,所以直接在后面进行数组拷贝
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
toString()方法:
因为ArrayList中没有toString方法,所以继承的是父类的父类AbstractCollection中的toString方法
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext())//如果没有元素
return "[]";
StringBuilder sb = new StringBuilder();//使用StringBuilder不用频繁创建String对象
sb.append('[');
for (;;) {//无限循环
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);//三目运算符
if (! it.hasNext())//如果没有元素
return sb.append(']').toString();
sb.append(',').append(' ');//如果有元素
}
}