一、ArrayList
ArrayList 實現 List、RandomAccess 接口。可以插入空數據,也支持隨機訪問。
ArrayList相當於動態數據,其中最重要的兩個屬性分別是: elementData 數組,以及 size 大小。 在調用
add() 方法的時候:
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
- 首先進行擴容校驗。
- 將插入的值放到尾部,並將 size + 1 。
如果是調用 add(index,e) 在指定位置添加的話:
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1);
//複製,向後移動
System.arraycopy(elementData, index, elementData, index + 1,size - index);
elementData[index] = element;
size++;
}
- 也是首先擴容校驗。
- 接著對數據進行復制,目的是把 index 位置空出來放本次插入的數據,並將後面的數據向後移動一個位置。
其實擴容最終調用的代碼:
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 的主要消耗是數組擴容以及在指定位置添加數據,在日常使用時最好是指定大小,儘量減少擴容。更要減少在指定位置插入數據的操作。
二、Vector
Vector 也是實現List 接口,底層數據結構和 ArrayList 類似,也是一個動態數組存放數據。不過是在 add() 方法的時候使用 synchronize 進行同步寫數據,但是開銷較大,所以 Vector 是一個同步容器並不是一個併發容器。
以下是 add() 方法:
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
以及指定位置插入數據:
public void add(int index, E element) {
insertElementAt(element, index);
}
public synchronized void insertElementAt(E obj, int index) {
modCount++;
if (index > elementCount) {
throw new ArrayIndexOutOfBoundsException(index
+ " > " + elementCount);
}
ensureCapacityHelper(elementCount + 1);
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
elementData[index] = obj;
elementCount++;
}
三、LinkedList
![Java集合框架(三)-ArrayList、Vector、LinkedList 的底層分析](http://p2.ttnews.xyz/loading.gif)
如圖所示 LinkedList 底層是基於雙向鏈表實現的,也是實現了 List 接口,所以也擁有 List 的一些特點。
add新增方法
public boolean add(E e) {
linkLast(e);
return true;
}
/**
* Links e as last element.
*/
void linkLast(E e) {
final Nodel = last;
final NodenewNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
可見每次插入都是移動指針,和 ArrayList 的拷貝數組來說效率要高上不少。
get查詢方法
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
Nodenode(int index) {
if (index < (size >> 1)) {
Nodex = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Nodex = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
由此可以看出是使用二分查找來看 index 離 size 中間距離來判斷,是從頭結點正序查還是從尾節點倒序查。
這樣的效率是非常低的,特別是當 index 距離 size 的中間位置越遠時。
總結:
- LinkedList 插入,刪除都是移動指針效率很高。
- 查找需要進行遍歷查詢,效率較低。
![Java集合框架(三)-ArrayList、Vector、LinkedList 的底層分析](http://p2.ttnews.xyz/loading.gif)
閱讀更多 nicky猿 的文章