学习数据结构的时候,我们就已经得出了结论
-
对于数组,随机元素访问的时间复杂度是
0(1)
, 元素插入操作是O(n)
; -
对于链表,随机元素访问的时间复杂度是
O(n)
, 元素插入操作是0(1)
.
元素插入对于链表来说应该是他的优势
但是他就一定比数组快? 我们执行插入1000w次的操作
private static void test(){
StopWatch stopWatch = new StopWatch();
int elementCount = 100000;
stopWatch.start("ArrayList add");
List<Integer> arrayList = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(ArrayList::new));
// ArrayList插入数据
IntStream.rangeClosed(0, elementCount).forEach(i ->arrayList.add(ThreadLocalRandom.current().nextInt(elementCount), 1));
stopWatch.stop();
stopWatch.start("linkedList add");
List<Integer> linkedList = IntStream.rangeClosed(1, elementCount).boxed().collect(Collectors.toCollection(LinkedList::new));
// ArrayList插入数据
IntStream.rangeClosed(0, elementCount).forEach(i -> linkedList.add(ThreadLocalRandom.current().nextInt(elementCount), 1));
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());
}
StopWatch '': running time = 44507882 ns
---------------------------------------------
ns % Task name
---------------------------------------------
043836412 098% elementCount 100 ArrayList add
000671470 002% elementCount 100 linkedList add
StopWatch '': running time = 196325261 ns
---------------------------------------------
ns % Task name
---------------------------------------------
053848980 027% elementCount 10000 ArrayList add
142476281 073% elementCount 10000 linkedList add
StopWatch '': running time = 26384216979 ns
---------------------------------------------
ns % Task name
---------------------------------------------
978501580 004% elementCount 100000 ArrayList add
25405715399 096% elementCount 100000 linkedList add
看到在执行插入1万、10完次操作的时候,LinkedList
的插入操作时间是 ArrayList
的两倍以上
那问题主要就是出现在linkedList
的 add()
方法上
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
/**
* Returns the (non-null) Node at the specified element index.
*/
Node<E> node(int index) {
// assert isElementIndex(index);
if(index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
linkedList
的 add()
方法主要逻辑
-
通过遍历找到那个节点的Node
-
执行插入操作
ArrayList
的 add()
方法
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++;
}
-
计算最小容量
-
最小容量大于数组对象,则进行扩容
-
进行数组复制,根据插入的index将数组向后移动一位
-
最后在空位上插入新值
根据试验的测试,我们得出了在实际的随机插入中,LinkedList
并没有比ArrayList
的速度快
所以在实际的使用中,如果涉及到头尾对象的操作,可以使用LinkedList
数据结构来进行增删的操作,发挥LinkedList
的优势
最好再进行实际的性能测试评估,来得到最合适的数据结构。