本文記錄ArrayList & LinkedList源碼解析 基於JDK1.8
ArrayList
ArrayList實現了List
接口 所有擁有List
接口所有方法 可以看成可'調節'的數組 可以包含任何類型數據(包括null,可重復)ArrayList
線程不是安全的
類結構
ArrayList類主要成員變量:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
// 初始化容量(數組初始長度) 10
private static final int DEFAULT_CAPACITY = 10;
// 表示空數組
private static final Object[] EMPTY_ELEMENTDATA = {};
// 也是空數組,和EMPTY_ELEMENTDATA區分開
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 裝ArrayList的數組
transient Object[] elementData; // non-private to simplify nested class access
// 數組長度
private int size;
}
方法解析
構造函數
public ArrayList()
public ArrayList() {
// elementData為DEFAULTCAPACITY_EMPTY_ELEMENTDATA。
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(int initialCapacity)
public ArrayList(int initialCapacity) {
// initialCapacity初始化容量 由調用者傳入
// 如果initialCapacity大於0
if (initialCapacity > 0) {
// elementData為 Object類型的 initialCapacity大小的數組
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 如果初始值為0 elementData為 EMPTY_ELEMENTDATA 空數組
this.elementData = EMPTY_ELEMENTDATA;
} else {
// initialCapacity 小於0 拋錯
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList(Collection<? extends E> c)
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}
創建一個包含指定集合c數據的ArrayList 上面為什么要多此一舉使用
Arrays.copyOf(elementData, size, Object[].class)復制一遍數組呢?這是因為在某些情況下
調用集合的toArray()方法返回的類型並不是Object[].class 比如:
Long[] array1 = {1L, 2L};
List<Long> list1 = Arrays.asList(array1);
Object[] array2 = list1.toArray();
System.out.println(array2.getClass() == Object[].class); // false
List<Long> list2 = new ArrayList<>();
System.out.println(list2.toArray().getClass() == Object[].class); // true
add方法
add(E e)向數組尾部添加元素
public boolean add(E e) {
// 確定數組容量 size 類型為int 默認值(即初始值)為 0
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
// minCapacity = size + 1 = 1
// elementData = {}
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// Math.max返回最大的數組 DEFAULT_CAPACITY = 10 minCapacity = 1 返回 10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// minCapacity = 10
// elementData = {}
// 10 - 0 > 0
if (minCapacity - elementData.length > 0)
// 擴容
grow(minCapacity);
}
private void grow(int minCapacity) {
// oldCapacity = 0
int oldCapacity = elementData.length;
// >> 右移運算符 相當於newCapacity為oldCapacity的1.5倍
// oldCapacity / 2 此處 0 + 0 / 2 還是等於 0
int newCapacity = oldCapacity + (oldCapacity >> 1);
// newCapacity = 0 minCapacity = 10 0 - 10 < 0 條件成立
if (newCapacity - minCapacity < 0)
// newCapacity = 10
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// // 復制到新數組 數組容量為10
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// MAX_ARRAY_SIZE常量值為Integer.MAX_VALUE - 8 通過
// 這段邏輯我們可以知道,ArrayList最大容量為Integer.MAX_VALUE
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
綜述 可以知道
- 任何一個空的數組 第一次添加元素的時候 內部數組容量將被擴容為10
- 擴容時,newCapacity為oldCapacity的1.5倍
- 容量最大值為Integer.MAX_VALUE - 8
- 尾部添加元素不用移動任何元素 所以速度快
add方法
add(int index, E element)用於在指定位置添加元素
public void add(int index, E element) {
// 數組下標檢查
rangeCheckForAdd(index);
// 跟上面add(E e) 一樣 確定數組容量
ensureCapacityInternal(size + 1); // Increments modCount!!
// 將原來index后面的所有元素往后面移動一個位置
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// index位置放入新元素
elementData[index] = element;
// 數組長度 + 1
size++;
}
private void rangeCheckForAdd(int index) {
// 如果下標大於數組長度 或者 小於0 拋錯
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
這里涉及到元素移動 所以速度較慢
get方法
public E get(int index) 獲取指定索引位置
public E get(int index) {
// 檢查index
rangeCheck(index);
// 直接返回數組指定位置元素
return elementData(index);
}
private void rangeCheck(int index) {
// 如果index 大於 數組長度 拋錯
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
直接返回索引所在元素 速度較快
set方法
set(int index, E element)向指定位置添加元素
public E set(int index, E element) {
// 檢查index 跟get方法一樣
rangeCheck(index);
// 獲取老的index所在位置元素
E oldValue = elementData(index);
// 設置新值
elementData[index] = element;
// 返回老值
return oldValue;
}
set方法不涉及元素移動和遍歷 所以速度快
remove方法
remove(int index)
public E remove(int index) {
// 檢查index 跟get方法一樣
rangeCheck(index);
modCount++;
// 獲取指定位置需要被刪除的元素
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
// elementData數組 index + 1的位置為復制起始點
// 從index位置開始 復制 numMoved個元素
// 即被刪除元素去除后 其后面的其他元素(如果有)都要向前移一位 覆蓋index處的元素
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 設置elementData最后一位元素為 null
elementData[--size] = null; // clear to let GC do its work
return oldValue;
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
# 參數解釋
# src 原數組 顧名思義 從哪個數組復制
# srcPos 從原數組哪個位置開始復制
# dest 目標數組
# destPos 目標數組起始位置
# length 復制的長度
}
remove(Object o)
// 區分 null和非null的元素 都是通過fastRemove方法來刪除
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
// 刪除邏輯和remove(int index)一樣 覆蓋index位置元素
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
LinkedList
類結構
LinkedList底層采用雙向鏈表結構存儲數據 允許重復數據和null值 長度沒有限制
每個節點用內部類Node表示
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;
}
}
其中包含 item(存儲數據)、next(后繼節點)、prev(前繼節點) Node可以分布在各個內存位置 通過prev 和 next來維護
LinkedList
包含如下成員變量
// 元素個數 默認為 0
transient int size = 0;
// 頭節點 該節點必須滿足(first == null && last == null) || (first.prev == null && first.item != null)
// 即頭節點滿足 前繼節點為 null 且item(存儲數據)不為空
transient Node<E> first;
// 尾節點 該節點必須滿足(first == null && last == null) || (last.next == null && last.item != null)
// 即尾節點滿足 后繼節點為 null 且item(存儲數據)不為空
transient Node<E> last;
方法解析
構造函數
public LinkedList()
public LinkedList() {}
空參構造函數 size 默認 為 0 每次添加新元素都需要創建Node類型節點
public LinkedList(Collection<? extends E> c)
public LinkedList(Collection<? extends E> c) {
// 調用空參構造函數
this();
addAll(c);
}
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
// 檢查index 必須要大於 0 且小於 size 否則拋錯
checkPositionIndex(index);
// 將c轉成一個數組
Object[] a = c.toArray();
// 獲取數組的長度
int numNew = a.length;
if (numNew == 0)
return false;
// 定義Node類型節點 pred succ
Node<E> pred, succ;
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
// 循環創建節點,設置prev,next指向
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
add方法
add(int index, E element)
public void add(int index, E element) {
// 檢查index 同構造函數
checkPositionIndex(index);
// 如果index 和 鏈表大小相同
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
void linkLast(E e) {
// 定義一個Node類型變量 獲取最后一個節點元素
final Node<E> l = last;
// 實例化Node類 prev為原鏈表最后一個節點 next為null
final Node<E> newNode = new Node<>(l, e, null);
// 將新節點賦值給last 成為 最后一個節點
last = newNode;
if (l == null)
// 如果老的鏈表中 最后一個節點為null 證明是一個空鏈表
// 此時 第一個節點和最后一個節點是相同的
first = newNode;
else
// l作為老的最后一個節點 在新節點生成后 需要將newNode 賦值給 老的next(后繼節點)
l.next = newNode;
// 元素個數 + 1
size++;
modCount++;
}
// 采用二分法遍歷每個Node節點 直到找到index位置的節點
Node<E> node(int index) {
// size >> 1 相當於 size / 2
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;
}
}
void linkBefore(E e, Node<E> succ) {
// succ為原鏈表指定index位置的節點 獲取其prev節點
final Node<E> pred = succ.prev;
// 創建新節點 prev 為原鏈表指定index位置節點的prev next為原鏈表指定index位置節點
final Node<E> newNode = new Node<>(pred, e, succ);
// 將原鏈表指定index位置的節點的prev更新為新節點
succ.prev = newNode;
// 如果succ為第一個節點
if (pred == null)
// 將新節點賦值給第一個節點
first = newNode;
else
// 將新節點賦值給原鏈表指定index節點的prev(上一個節點)的next
pred.next = newNode;
// 元素個數 + 1
size++;
modCount++;
}
get方法
get(int index)
public E get(int index) {
// 檢查索引
checkElementIndex(index);
// 利用二分法查詢index位置所在節點 返回其item值
return node(index).item;
}
set方法
set(int index, E element)
public E set(int index, E element) {
// 檢查索引
checkElementIndex(index);
// 獲取index位置所在節點
Node<E> x = node(index);
// 獲取節點的item值
E oldVal = x.item;
// 將item賦值為新值
x.item = element;
// 返回舊的值
return oldVal;
}
remove方法
remove(int index)
public E remove(int index) {
// 檢查索引
checkElementIndex(index);
// node(index)返回當前index對應的節點
return unlink(node(index));
}
E unlink(Node<E> x) {
// 獲取index位置所在節點item值
final E element = x.item;
// 獲取index位置所在節點 后繼節點
final Node<E> next = x.next;
// 獲取index位置所在節點 前繼節點
final Node<E> prev = x.prev;
// 刪除的是否是頭節點
if (prev == null) {
// 頭節點賦值為 index位置所在節點的下一個節點(index所在節點要被刪了)
first = next;
} else {
// 不是頭節點 需要將index位置所在節點的上一個節點的next指向 下一個節點
// index位置所在節點prev置為 null
prev.next = next;
x.prev = null;
}
// 刪除的是否是最后一個節點
if (next == null) {
// 最后一個節點賦值為index位置所在節點的上一個節點
last = prev;
} else {
// 不是刪除最后一個節點
// index位置所在節點的下一個節點的prev指向上一個節點
next.prev = prev;
x.next = null;
}
// item置為
x.item = null;
// 元素個數 - 1
size--;
modCount++;
// 返回被刪除元素
return element;
}
RandomAccess接口
RandomAccess接口是一個空接口即標記接口
public interface RandomAccess {
}
實現該接口的類說明其支持快速隨機訪問 比如ArrayList實現了該接口 說明ArrayList支持快速隨機訪問 所謂快速隨機訪問指的是通過元素的下標即可快速獲取元素對象 無需遍歷 而LinkedList則沒有這個特性 元素獲取必須遍歷鏈表