本人能力有限,入行时间短,Java开发方面的基本转载,顺便记载自己的所学,以后有不当之处,本人会及时修改,一方面方便自己查阅,另一方面望对各位尽微薄之力。
本篇讲述的是列表的“写”操作,包括插入、删除、修改操作。
(1)插入元素
列表中我们使用最多的是ArrayList,下面来看看它的插入(add方法)算法,源代码如下:
public void add(int index, E element) {
/*
* 检查下标是否越界
*/
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
//若需要扩容,则增大底层数组的长度
ensureCapacityInternal(size + 1); // Increments modCount!!
//给index下标之后的元素(包括当前元素)的下标+1,空出index的位置
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//给index位置的元素赋值
elementData[index] = element;
//列表长度+1
size++;
}
ArrayList的插入算法重点在arraycopy方法,从源代码可以看出ArrayList只要是插入一个元素,其后的所有元素都会向后移动一位。虽然arraycopy是一个本地方法,效率也非常高,但频繁的插入,都会使后面所有的元素进行拷贝,效率自然就变低了,特别是头位置插入元素时。不过,在开发中确实遇到插入元素的情况,可以使用LinkedList解决效率低的问题。LinkedList是一个双向链表的,它的插入只是修改相邻元素的next和previous的引用,其插入算法(add方法)源代码如下:
public void add(int index, E element) {
//检查index位置的元素
checkPositionIndex(index);
if (index == size)
//如果index的位置是列表的长度
linkLast(element);
else
linkBefore(element, node(index));
}
这是一个典型的双向链表的插入算法,把自己插入到链表,没有任何的元素会有拷贝的过程,只是引用地址改变了,效率自然提高了。经过实际测试,LinkedList的插入效率比ArrayList的快50倍以上。
(2)删除元素
ArrayList提供了删除指定位置上的元素、删除指定值的元素、删除一个下标范围内的元素集等删除动作,三者的实现原理基本类似,都是找到索引位置,然后删除。我们以最常用的删除指定下标位置元素的方法(remove方法)为例来看看ArrayList删除操作的性能,源代码如下:
public E remove(int index) {
//检查index位置的下标
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
//修改计数器+1
modCount++;
//记录要删除元素的值
E oldValue = (E) elementData[index];
//记录需要向前移动的元素数量
int numMoved = size - index - 1;
if (numMoved > 0)
//index位置后的所有元素都向前移动一位
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//原列表的长度-1,并且最后一位设为null
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
通过源代码可以看出ArrayList的删除操作同样进行了数组拷贝,如果数据量过大的话,必然引起性能和效率低下的问题。
我们再来看LinkedList的删除操作,LinkedList提供了非常多的列表删除操作方法,比如删除指定位置的元素、删除头元素等,与之相关的poll方法也会执行删除操作,删除操作同样没有任何的耗时过程,全部都是引用指针的变更,效率自然就会高了。在实际测试中得知,处理大批量的删除列表操作,LinkedList的效率比ArrayList快40倍以上。
(3)修改元素
在列表的修改元素方面,由于LinkedList是顺序存取的,因此定位元素必然是一个遍历的过程,效率大打折扣,远不如ArrayList。我们来看看LinkedList的set方法源代码:
public E set(int index, E element) {
//检查index位置的元素
checkElementIndex(index);
//定位节点
Node<E> x = node(index);
E oldVal = x.item;
//节点的元素替换
x.item = element;
return oldVal;
}
LinkedList顺序存储列表的元素定位方式会折半遍历,这是一个极耗时的操作。而ArrayList的修改操作则是数组元素的直接替换,简单高效。
在修改操作上,LinkedList要不ArrayList慢很多,特别是要进行数据量比较大的修改时,两者肯定完全不在一个数量级上。
总之,LinkedList在删除和插入列表元素方面效率高,ArrayList在修改列表元素方面效率高。
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
两者如何选择,依赖于诸位正在开发中的系统,如果是一个实时交易系统,即使写操作再少,使用LinkedList也比ArrayList合适,因为此类系统是争分夺秒的,多个N个毫秒可能就会造成交易数据不准确;而对于一个批量系统来说,几十毫秒、几百毫秒,甚至是几千毫秒的差别意义都不大,此时使用LinkedList还是ArrayList凭个人喜好,当然,如果系统已经处于性能临界点了那就必须使用LinkedList。