这次来从源码角度分析一下LinkedList与ArrayList的addAll方法
之前有研究过LinkedList和ArrayList的remove()方法源码,发现二者分别是基于双向链表和一个数组进行实现的ADT,在后面的开发中,我也就天真的认为LinkedList是直接让list的后继元等于要加的list的第一个元素的引用就完事了,通过对源码的分析,事实并非如此。
First of all, 注意两种集合类的addAll方法的参数都是Collection接口而非具体的实现类
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;
}
对于ArrayList来讲,先通过ensureCapacityInternal()方法提高elementData数组的长度,然后通过native方法System.arraycopy()将新的list拼到原数组尾部并放到elementData中,猜测底层上应该也是一个一个遍历存放的,但是由于是native方法,效率应该是比较高的。
public boolean addAll(Collection extends E> c) {
return addAll(size, c);
}
....
public boolean addAll(int index, Collection extends E> c) {
checkPositionIndex(index);
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
Node pred, succ;
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
LinkedList的addAll(Collection)方法本质上是调用了addAll(int, Collection)方法,可以看到,该方法中首先将要添加的list转化为了Object[]:
Object[] a = c.toArray();
之后通过增强for遍历这个数组挨个维护前驱元与后继元
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
由于转化为数组和对前驱元与后继元的挨个维护,效率上应该是不如ArrayList的addAll的。
这个问题笔者也困扰,查阅了相关的资料,简单的总结了下:
如果您有确定的答案,很希望您能联系我
我的博客