省流
1. 类的声明
public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
- ArrayList继承自AbstractList;实现了List接口。
- 实现了RandomAccess接口,支持随机访问随机读取,对于能够用索引实现数据的查询修改需要实现该接口。
- 实现Cloneable接口,支持克隆。
-
2. 成员变量
```java /**
Default initial capacity. */ // 初始容量,默认扩容阈值 private static final int DEFAULT_CAPACITY = 10;
/**
Shared empty array instance used for empty instances. */ private static final Object[] EMPTY_ELEMENTDATA = {};
/**
- Shared empty array instance used for default sized empty instances. We
- distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
first element is added. */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
- The array buffer into which the elements of the ArrayList are stored.
- The capacity of the ArrayList is the length of this array buffer. Any
- empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
- will be expanded to DEFAULT_CAPACITY when the first element is added. / /
- transient: 不能序列化;
- 实现了Serializable接口,主题数组elementData却又不支持序列化;ArrayList重写了序列化接口 */ transient Object[] elementData; // non-private to simplify nested class access
/**
- The size of the ArrayList (the number of elements it contains). *
- @serial */ private int size;
1. `_DEFAULT_CAPACITY _`_:_ 数组默认扩容大小。(不一定第一次扩容就是10,具体原因后面详解)1. `_DEFAULTCAPACITY_EMPTY_ELEMENTDATA_ `和 `_EMPTY_ELEMENTDATA_`_:_默认初始化的时候的两个空数组,具体用哪个,有条件。(这里和1中的扩容也有关系)1. `transient Object[] elementData;` :这个就是ArrayList底层的数组。(为什么要用transient修饰?既然支持序列化,但数组对象缺用不可序列化关键字修饰)1. `size`:数组的元素个数,不是当前的容量。<a name="pjvPM"></a># 3. 构造方法```javapublic 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);}}
- 带参构造,参数为设定初始容量大小。
由
else if语句中可知,当带参构造的参数为0的时候,用到了两个空数组中的_EMPTY_ELEMENTDATA_,这里直接影响到了后面的扩容方法。(详情参见:ensureCapacityInternal方法)public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}
这里就是直接给
elementData赋值为_DEFAULTCAPACITY_EMPTY_ELEMENTDATA_4. 关键方法(常考)
4.1 trimToSize()
4.2 扩容
```java private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
// 和快速失败相关,在AbstractList中modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);
}
/**
- The maximum size of array to allocate.
- Some VMs reserve some header words in an array.
- Attempts to allocate larger arrays may result in
OutOfMemoryError: Requested array size exceeds VM limit */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
- Increases the capacity to ensure that it can hold at least the
- number of elements specified by the minimum capacity argument. *
@param minCapacity the desired minimum capacity */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; // 移位运算右移一位,等于除二取整。所以扩容大小约等于1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :MAX_ARRAY_SIZE;
}
```
add方法会先执行ensureCapacityInternal方法,传入的参数是当前数组元素个数+1
(size+1(minCapacity 当前所需的最小容量));
ensureCapacityInternal中会进行一次calculateCapacity,- 如果当前的
elementData是_DEFAULTCAPACITY_EMPTY_ELEMENTDATA_则calculateCapacity会比较minCapacity和_DEFAULT_CAPACITY_,返回两者中的大值。所以,无参构造,第一次扩容后的容量为_DEFAULT_CAPACITY_。 - 如果当前的
elementData是_EMPTY_ELEMENTDATA_(有参构造,传入参数0时),则calculateCapacity会直接返回minCapacity。
- 如果当前的
- 当
minCapacity大于elementData.length时,走扩容方法void grow(int minCapacity)。- 扩容大小大约为
elementData.length的1.5倍oldCapacity + (oldCapacity >> 1);这里做了移位运算,右移一位等于除以2向下取整。移位运算对计算机来说更快。 elementDate的最大长度为Integer._MAX_VALUE _- 8减8是为了存储数组长度;Arrays._copyOf_方法待补充
- 扩容大小大约为
经过上面分析可知,如果ArrayList的构造参数为0时,扩容方法就会按每次1.5倍进行扩容。这时候,在数组中添加10个元素,就会进行7次扩容!!!
4.3 修改方法
插入删除操作时,有数组的移位。JDK里面都是用的
public static native void arraycopy(Object src, int srcPos,Object dest, int destPos, int length);方法来实现,说明在生产时,大List拷贝操作,可以考虑使用该方法。public E set(int index, E element)set(remove也一样)方法是有返回值的,返回值为该位置上的被替换元素(oldValue);public E remove(int index)remove方法中,删除元素后会进行一次elementData[--size] = null;操作,目地时为了让JVM进行GC。public void clear()clear方法,只是在吧elementData所有位置置为null,并没有重置elementData,所以当一个List在clear之后,重新添加元素执行add,在超过原有容量前不会进行扩容。public boolean removeAll(Collection<?> c)方法第一行对参数c使用Objects._requireNonNull_(c);进行null值判断,但是没有判空,所以如果是空的参数c,则会走batchRemove方法判断一次,有性能损耗。4.4 序列化
private void writeObject(java.io.ObjectOutputStream s)Arrays 内部实现了一个私有ArrayList类,所以可以用List接收;
- 但是Arrays 的私有ArrayList类没有重写父类(AbstractList)的add方法。导致直接调用父类(AbstractList)中的add方法:throw new UnsupportedOperationException();
在List的大小不再改变的场景下(不再增加和减少),使用Arrays.asList();因为它没有add 和 remove方法,确保数组大小不会改变。
5.2 Collections.emptyList();
这块代码的使用场景是:当有需要返回空List的时候;
- 因为:
Collections._emptyList_();无需进行新对象的创建;JVM再进行编译的时候已经对final关键字的对象进行了创建。public static final <T> List<T> emptyList()
