省流
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. 构造方法
```java
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);
}
}
- 带参构造,参数为设定初始容量大小。
由
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 code
if (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()