2、增加元素到列表的任意位置:
List接口还提供了在任意位置插入元素的方法:void add(int index,E element)方法,由于实现方式不同,ArrayList和LinkedList在这个方法上存在一定的差异。由于ArrayList是基于数组实现的,而数组是一块连续的内存,如果在数组的任意位置插入元素,必然会导致该位置之后的所有元素重新排序,其效率相对较低。
ArrayList源码实现:
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
可以看出每次插入都会进行数组复制,大量的数组复制操作导致系统性能效率低下。并且数组插入的位置越靠前,数组复制的开销就越大。因此,尽可能插入元素在其尾端附近,有助于提高该方法的性能。
LinkedList的源码实现:
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
void linkBefore(E e, Node succ) {
// assert succ != null;
final Node pred = succ.prev;
final Node newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
对于LinkedList的在尾端插入和对任意位置插入数据是一样的,并不会因为插入位置靠前而导致效率低下。因此,在应用中,如果经常往任意位置插入元素,可以考虑使用LinkedList提到ArrayList。
3、删除任意位置的元素:
List接口还提供了在任意位置删除元素的方法:remove(int index)方法。在ArrayList中,对于remove()方法和add()方法一样,在任意位置移除元素,都需要数组复制。
ArrayList的remove()方法的源码如下:
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
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
return oldValue;
}
可以看出,在ArrayList的每一次删除操作,都需要进行数组重组,并且删除元素的位置越靠前,数组重组的开销就越大。
LinkedList的remove()方法的源码:
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
E unlink(Node x) {
// assert x != null;
final E element = x.item;
final Node next = x.next;
final Node prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
Node node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
在LinkedList中首先通过循环找到要删除的元素,如果元素位于前半段则,从前往后找;若位置位于后半段,则从后往前找,但是要移除中间的元素,却几乎要遍历半个List。所有,无论元素位于较前还是较后,效率都比较高,但是位于中间效率就非常低。
4、容量参数:
容量参数是ArrayList和Vector等基于数组的List特有的性能参数,它表示初始化数组的大小。当数组所存储的元素的数量超过其原有的大小时,它就会进行扩容,即进行一次数组复制,因此,合理设置数组大小有助于减少扩容次数,从而提升系统性能。
5、遍历列表:
在JDK1.5之后,至少有三种遍历列表的方式:forEach操作,迭代器,for循环。通过测试发现,forEach综合性能不如迭代器,而for循环遍历列表时,ArrayList的性能表现最好,而LinkedList的性能差的无法忍受,因为LinkedList进行随机访问,总会进行一次列表的遍历操作。
对于ArrayList是基于数组来实现的,随机访问效率快,因此有限选择随机访问。而LinkedList是基于链表实现的,随机访问的性能差,应该避免使用。