浅谈ArrayList和LinkedList到底谁更快
一、ArrayList和LinkedList究竟谁快
在Java中应该都知道ArrayList和LinkedList,
一直以来的概念呢是
ArrayList在get(index)这个应该比LinkedList快;
LinkedList比ArrayList在add(index,element)快;
两者共同遍历呢,应该是一样快的,毕竟都要循环遍历一遍。
直到我写了一个测试类
package com.lw; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.junit.Test; public class TestJDKList { List<Integer> linkedList = new LinkedList<>(); List<Integer> arrayList = new ArrayList<>(); int length = 1000000; @Test public void testLinkedInsert(){ for (int i = 0; i < length; i++) { linkedList.add(i); } long currentMi2 = System.currentTimeMillis(); linkedList.add(length/2,3); long endTime2 = System.currentTimeMillis(); System.out.println("testLinkedInsert:" + (endTime2 - currentMi2)); // 9 } @Test public void testArrayInsert(){ for (int i = 0; i < length; i++) { arrayList.add(i); } long currentMi2 = System.currentTimeMillis(); arrayList.add(length/2,3); long endTime2 = System.currentTimeMillis(); System.out.println("testArrayInsert:" + (endTime2 - currentMi2)); // 1 } @Test public void testLinkedGet(){ for (int i = 0; i < length; i++) { linkedList.add(i); } long currentMi2 = System.currentTimeMillis(); linkedList.get(length/2); long endTime2 = System.currentTimeMillis(); System.out.println("testLinkedGet:" + (endTime2 - currentMi2)); // 5 } @Test public void testArrayGet(){ for (int i = 0; i < length; i++) { arrayList.add(i); } long currentMi2 = System.currentTimeMillis(); arrayList.get(length/2); long endTime2 = System.currentTimeMillis(); System.out.println("testArrayGet:" + (endTime2 - currentMi2)); // 0 } @Test public void testLinkedIter(){ for (int i = 0; i < length; i++) { linkedList.add(i); } long currentMi2 = System.currentTimeMillis(); for (Integer i : linkedList) { }; long endTime2 = System.currentTimeMillis(); System.out.println("testLinkedIter:" + (endTime2 - currentMi2)); // 26 } @Test public void testArrayIter(){ for (int i = 0; i < length; i++) { arrayList.add(i); } long currentMi2 = System.currentTimeMillis(); for (Integer i : arrayList) { }; long endTime2 = System.currentTimeMillis(); System.out.println("testArrayIter:" + (endTime2 - currentMi2)); // 11 } @Test public void testLinkedAdd() { long currentMi2 = System.currentTimeMillis(); for (int i = 0; i < length; i++) { linkedList.add(i); } long endTime2 = System.currentTimeMillis(); System.out.println("testLinkedAdd:" + (endTime2 - currentMi2)); // 53 } @Test public void testArrayAdd(){ long currentMi1 = System.currentTimeMillis(); for (int i = 0; i < length; i++) { arrayList.add(i); } long endTime1 = System.currentTimeMillis(); System.out.println("testArrayAdd:" + (endTime1 - currentMi1)); // 35 } }
二、结果
运行了两遍结果如下:
testLinkedInsert:7
testArrayInsert:0
testLinkedAdd:218
testArrayAdd:23
testLinkedGet:4
testArrayGet:0
testLinkedIter:14
testArrayIter:11
----------------第二遍分割线---------------------------------
testLinkedInsert:12
testArrayInsert:0
testLinkedIter:13
testArrayIter:12
testLinkedGet:3
testArrayGet:0
testLinkedAdd:119
testArrayAdd:23
颠覆三观,ArrayList竟然无论怎样都比LinkedList快??
三、循环Add
ArrayList的add源码,它是把数据放在一个数组中
transient Object[] elementData;
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
而LinkedList源码,是把数据放在Node对象中,有个前后指针。
public boolean add(E e) { linkLast(e); return true; } void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }
private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
难道是前后指针这里花时间了么?
四、指定位置Get
再看get方法,
ArrayList的get,因为是连续的内存,所以取数据很快。
public E get(int index) { rangeCheck(index); return elementData(index); } E elementData(int index) { return (E) elementData[index]; }
再看LinkedList的get,是通过指针遍历的,直到是这个index为止。
这里还有判断size,如果是size的前一半,则通过first节点往后去找,如果在后一半则通过last节点往前找,这样会更快,所以LinkedList的查找其实也不慢。
public E get(int index) { checkElementIndex(index); return node(index).item; } 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; } }
五、指定位置Add
ArrayList的add(index,element)
这里是可以扩容的,将index后半段拷贝到index+1,然后在index插入一个新的,但没想到这么快。
其实也能想到System.arraycopy是native,所以快也能理解
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的add(index,element)
无非是指针的指向变化而已,但没想到比上面的System.arraycopy还要慢,果然不愧为native方法。
public void add(int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); } void linkBefore(E e, Node<E> succ) { // assert succ != null; final Node<E> pred = succ.prev; final Node<E> newNode = new Node<>(pred, e, succ); succ.prev = newNode; if (pred == null) first = newNode; else pred.next = newNode; size++; modCount++; } void linkLast(E e) { final Node<E> l = last; final Node<E> newNode = new Node<>(l, e, null); last = newNode; if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }
所以项目中大部分用ArrayList也就是可以理解。
不过ArrayList是连续的内存空间,在内存空间很紧张情况下,LinkedList内存利用率更高。
到此这篇关于浅谈ArrayList和LinkedList到底谁更快的文章就介绍到这了,更多相关ArrayList和LinkedList内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!