1.概述
作为程序员,一定很熟悉 ArrayList
, 可调整大小的数组的实现List
接口。实现所有可选列表操作,并允许所有元素,包括null
。除了实现List 接口
之外,该类还提供了一些方法来操纵内部使用的存储列表的数组的大小。(这个类是大致相当于Vector,
不同之处在于它是不同步的)。
该size,isEmpty,get,set,iterator
和listIterator
操作在固定时间内运行。 add
操作以摊余常数运行 ,即添加n个元素需要O(n)个时间。 所有其他操作都以线性时间运行(粗略地说)。 与LinkedList
实施相比,常数因子较低。
每个ArrayList
实例都有一个容量 。 容量是用于存储列表中的元素的数组的大小。 它总是至少与列表大小一样大。 当元素添加到ArrayList时,其容量会自动增长。 没有规定增长政策的细节,除了添加元素具有不变的摊销时间成本。
应用程序可以添加大量使用ensureCapacity
操作元件的前增大ArrayList
实例的容量。 这可能会减少增量重新分配的数量。
请注意,此实现不同步。 如果多个线程同时访问884457282749
实例,并且至少有一个线程在结构上修改列表,则必须 在外部进行同步。 (结构修改是添加或删除一个或多个元素的任何操作,或明确调整后台数组的大小;仅设置元素的值不是结构修改。)这通常是通过在一些自然地封装了列表。 如果没有这样的对象存在,列表应该使用Collections.synchronizedList
方法“包装”。 这最好在创建时完成,以防止意外的不同步访问列表:
Copy List list = Collections.synchronizedList(new ArrayList(...));
The iterators returned by this class's个 iterator
和listIterator
方法是快速失败的 :如果列表在任何时间从结构上修改创建迭代器之后,以任何方式除非通过迭代器自身remove
种或add
方法,迭代器都将抛出一个ConcurrentModificationException
。 因此,面对并发修改,迭代器将快速而干净地失败,而不是在未来未确定的时间冒着任意的非确定性行为。
请注意,迭代器的故障快速行为无法保证,因为一般来说,在不同步并发修改的情况下,无法做出任何硬性保证。 失败快速迭代器尽力投入ConcurrentModificationException
。 因此,编写依赖于此异常的程序的正确性将是错误的:迭代器的故障快速行为应仅用于检测错误。
2.源码分析
2.1 主要成员变量
默认长度
Copy private static final int DEFAULT_CAPACITY = 10;
当构造时参数为 0 指定的集合
Copy private static final Object[] EMPTY_ELEMENTDATA = {};
空的构造方法时使用
Copy private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
集合元素的缓冲区
Copy transient Object[] elementData;
2.2 构造方法
无参构造
Copy public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
有参构造
Copy 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);
}
}
这里对入参进行了校验,根据参数的不同进行不同的处理
Copy 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 {
this.elementData = EMPTY_ELEMENTDATA;
}
}
2.3 添加方法
add(E e)
Copy public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
private Object[] grow() {
return grow(size + 1);
}
从上面的代码我们可以看出,当执行add()时,首先会对父类AbstractList
元素进行++ 操作, 此变量主要是为了检查是否存在共修改,最终会导致throw new ConcurrentModificationException()
当执行add(e, elementData, size)
时,首先会判断elementData.length
与size
是否相等, grow()
主要时对长度进行加一的操作,(这里我们也可以看出,为什么说,ArrayList
是线程不安全的, 当初线程同时添加数据,size+1
会失去原子操作,导致数据出现问题)接下来就是对指定下标的进行赋值, 并对size进行赋值;
add(int index, E element)
Copy public void add(int index, E element) {
rangeCheckForAdd(index);
modCount++;
final int s;
Object[] elementData;
if ((s = size) == (elementData = this.elementData).length)
elementData = grow();
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
elementData[index] = element;
size = s + 1;
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
rangeCheckForAdd
只要对下标进行校验,剩下的就是和之前的差不多了,只不过这里用了System.arraycopy,比较耗性能
set(int index, E element)
Copy public E set(int index, E element) {
Objects.checkIndex(index, size);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
当我们执行set时,首先也会对下标进行校验,首先根据指定下标获取到要替换值,再根据下标进行从新赋值,再将原有的旧值进行返回
remove(int index)
Copy public E remove(int index) {
Objects.checkIndex(index, size);
final Object[] es = elementData;
@SuppressWarnings("unchecked") E oldValue = (E) es[index];
fastRemove(es, index);
return oldValue;
}
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
remove(Object o)
Copy public boolean remove(Object o) {
final Object[] es = elementData;
final int size = this.size;
int i = 0;
found: {
if (o == null) {
for (; i < size; i++)
if (es[i] == null)
break found;
} else {
for (; i < size; i++)
if (o.equals(es[i]))
break found;
}
return false;
}
fastRemove(es, i);
return true;
}
此方法是根据给的的元素进行删除,由于集合内可以存在null
,所以当我传递为null
直接跳出循环 这里说明一下,found: {}
是一个代码块,我们可以直接跳出次代码块,通过found: {}
我们可以计算出 要删除元素的下标,再次执行fastRemove(es, i)
方法,当集合内存在多个元素且值完全相同,只会删除第一个元素,执行的过程和上边的根据下标删除类似
关于ArrayList还有很多方法,比如set() clear().....
,如果你掌握了上边的源码,再看其他的方法也是类似的
总结
ArrayList 是实现不安全的,在add(),remove()
方法既可以看出
ArrayList 查询的速度非常快,它可以根据下标快速定位,但是新增的效率就相对耗时的多(System.arraycopy)