ArrayList 實現於 List、RandomAccess 接口。能夠插入空數據,也支持隨機訪問。java
ArrayList至關於動態數據,其中最重要的兩個屬性分別是: elementData 數組,以及 size 大小。 在調用 add() 方法的時候:node
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
若是是調用 add(index,e) 在指定位置添加的話:數組
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++; }
其實擴容最終調用的代碼:dom
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
也是一個數組複製的過程。性能
因而可知 ArrayList
的主要消耗是數組擴容以及在指定位置添加數據,在平常使用時最好是指定大小,儘可能減小擴容。更要減小在指定位置插入數據的操做。this
如圖所示 LinkedList 底層是基於雙向鏈表實現的,也是實現了 List 接口,因此也擁有 List 的一些特色(JDK1.7/8 以後取消了循環,修改成雙向鏈表)。spa
public boolean add(E e) { linkLast(e); return true; } /** * Links e as last element. */ 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 的拷貝數組來講效率要高上很多。指針
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; } }
上述代碼,利用了雙向鏈表的特性,若是index
離鏈表頭比較近,就從節點頭部遍歷。不然就從節點尾部開始遍歷。使用空間(雙向鏈表)來換取時間。code
這樣的效率是很是低的,特別是當 index 越接近 size 的中間值時。blog
總結: