ArrayList 是什么?
一切从源码出发:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
从代码可以看出:
1. Arraylist实现了List接口,因此ArrayList是一个列表,一个动态数组。
2. RandomAccess 是一个标志,本次不做分析
3. 实现了Cloneable接口,因此可以调用object.clone()实现对象的浅拷贝
4. 继承了AbstractList抽象类,它提供了一部分接口的实现
//默认初始容量
private static final int DEFAULT_CAPACITY = 10;
//一个空实例
private static final Object[] EMPTY_ELEMENTDATA = {};
//一个空实例
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//一个数组对象,实际存储值对象的地方
transient Object[] elementData;
//elementData里面存储对象的个数
private int size;
可以看到ArrayList本质上还是对一个数组的包装,为一个动态数组,使用数组elementData存储参数。elementData的类型为Object[],因此ArrayList的泛型的实现,本质上都是对Object的装箱和拆箱而已,因此可能会带来一些性能上的损失。
构造函数
//initialCapacity 为用户指定初始容量,必须大于或等于0
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//设置为指定大小的Object数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//初始值设为EMPTY_ELEMENTDATA
this.elementData = EMPTY_ELEMENTDATA;
} else {
//initialCapacity<0时,抛错
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//未指定大小则赋值为一个空数组对象
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//设置初始值
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
//初始值大小不等于0时
if ((size = elementData.length) != 0) {
// c.toArray 返回的不是object[]对象时,需要进行装箱操作
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 初始值大小为0时,初始值替换为EMPTY_ELEMENTDATA
this.elementData = EMPTY_ELEMENTDATA;
}
}
EMPTY_ELEMENTDATA 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA都是为空数组实例,区别后面或说明。
增操作
add(E e) 方法
//添加一个对象到尾部
public boolean add(E e) {
//确保当前容量足够,不够则进行扩容操作
ensureCapacityInternal(size + 1); // Increments modCount!!
//设置值e
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
//初始值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA时
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//当前需要容量不足DEFAULT_CAPACITY(10)时,直接扩为10
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//确保得到明确需要新容量
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
//“发生结构”时+1,防止遍历元素时,发生结构变更,则抛错。
modCount++;
// 需要容量大于当前容量时,执行扩容操作
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
可以看到:但只有初始值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,第一次添加元素个数小于DEFAULT_CAPACITY时,直接初始elementData的大小为DEFAULT_CAPACITY。
而初始值为EMPTY_ELEMENTDATA,会遵循扩容规则:
//minCapacity 当前需要的最小容量
private void grow(int minCapacity) {
// 当前容量
int oldCapacity = elementData.length;
//新容量=当前容量+当前容量的1/2
int newCapacity = oldCapacity + (oldCapacity >> 1);
//当新容量还是小于minCapacity时,则直接扩从至minCapacity大小
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//当newCapacity大于MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8)时
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 容量过于巨大,需判断是否发生溢出
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// 本质还是调用Arrays内方法,完成对数组的操作!
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // 发生内存溢出,
throw new OutOfMemoryError();
//未发生溢出,则返回 Integer.MAX_VALUE 或 MAX_ARRAY_SIZE
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
add(int index, E element) 方法
//入一个元素至指定位置
public void add(int index, E element) {
//index范围检查
rangeCheckForAdd(index);
//确保容量
ensureCapacityInternal(size + 1); // Increments modCount!!
//index位置及之后的都向后顺移动一位
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//设置index位置的值为element
elementData[index] = element;
//数量+1
size++;
}
//0<=index<size校验
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
addAll 方法
//批量插入元素至尾部,无非是在确保容量足够后,调用System.arraycopy拷贝c中的所有元素至elementData尾部,不在赘述
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
//从指定位置开始,批量插入元素。基本同上面一致,不在赘述
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
删操作
remove方法
//删除指定位置的元素
public E remove(int index) {
//范围检查
rangeCheck(index);
modCount++;
//获取index位置的值
E oldValue = elementData(index);
//计算需要移动元素的个数
int numMoved = size - index - 1;
if (numMoved > 0)
//index位置之后所有元素迁移一位
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//size-1,且最后一位置为null
//(置为null,释放当前引用,当对象无其他引用时,会被GC标记回收)
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
//删除指定元素
public boolean remove(Object o) {
if (o == null) {
//o为null时,则删除所有数组的null元素
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
//o不为null时,删除所有equals为true的元素
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
//删除指定位置index的元素,且不用在对index范围做校验
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
removeAll方法
//批量删除
public boolean removeAll(Collection<?> c) {
// c 不能为null
Objects.requireNonNull(c);
//调用batchRemove,批量 删除指定元素
return batchRemove(c, false);
}
//complement为ture时,删除在c中的元素;false时,删除所有不在c中元素
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
//将符合符合条件的元素前移,w计算,r则为当前遍历的位置
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
//当c.contains()发生异常时,r<size
if (r != size) {
//r之后的元素迁移到之后,w个数增加
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// w位置之后置为null,为了GC
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
//当前size为w
size = w;
modified = true;
}
}
//结构发生变更
return modified;
}
retainAll方法
//删除所有不在c中的元素,不在赘述!
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
clear方法
//置elementData中所有值为null,注意不会变更elementData的大小,如果需要释放,请调用trimToSize方法
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
改操作
set方法
//设置指定位置的值,一目了然
public E set(int index, E element) {
//index范围检查
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
查操作
get方法
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
indexOf方法
//查找o在elementData中第一次出现的位置,通过对elementData遍历实现,很简单,不在赘述
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
//很简单,不在赘述
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
其他操作
iterator方法
//遍历器,具体实现,请看后面的博客
public Iterator<E> iterator() {
return new Itr();
}
subList方法
//得到elementData的一个子数组,备注:实际上只是做了一个简单包装,实际操作的还是elementData
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
forEach方法
//通过实现一个Consumer实例,对elementData每个元素,执行action.accept操作
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
//遍历元素
for (int i=0; modCount == expectedModCount && i < size; i++) {
//执行accept操作
action.accept(elementData[i]);
}
//遍历元素时发生结构变更操作,则抛错!
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
//例如遍历输出每个元素
List<String> arrs = new ArrayList<>();
arrs.add("a");
arrs.add("b");
Consumer<String> outprint = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println("S:"+s);
}
};
arrs.forEach(outprint);
spliterator方法
// 这是jdk8为了适应并行操作而新增一个方法,类似于fork/join场景,把一个任务分成多个任务,具体详解可参考后面的博客。
@Override
public Spliterator<E> spliterator() {
return new ArrayListSpliterator<>(this, 0, -1, 0);
}
replaceAll方法
@Override
@SuppressWarnings("unchecked")
public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
//记录modCount值,用于后面判断是否发生变更
final int expectedModCount = modCount;
final int size = this.size;
//遍历元素,并且执行operator.apply方法后,将返回值设置替换当前位置的值
for (int i=0; modCount == expectedModCount && i < size; i++) {
elementData[i] = operator.apply((E) elementData[i]);
}
//发生结构变更,则报错
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
replaceAll和forEach方法基本类似,不同之处在于是否需要返回值及进行处理。
sort方法
//实现一个Comparator比较器,对ArrayList元素进行排序
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
//排序时发生结构变更,则报错
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
小结
总体来说:
1.ArrayList本质上是一个动态数组,对它的操作很多也是直接调用Arrays类直接进行处理的。
2.ArrayList的泛型的实现本质上是对object的装箱和拆箱,在性能上会有一定的损失。
3.对于大批量的插入操作来说,最好先定义一个合适的长度,防止扩容带来的性能损失
4.ArrayList 通过modCount来保证迭代器是快速失败的。
5.ArrayList 对于元素的查找本质上还是对数组进行遍历,并不比我们直接遍历来快。同时,往指定位置插入元素,性能也是不高的。