ArrayList源碼解析

ArrayList源碼解析

閒來無事,有空會繼續寫下
List
LinkedList
CopyOnWriteArrayList
Set
HashSet
LinkedHashSet
Map
HashMap
IdentityHashMap
LinkedHashMap
TreeMap
ConcurrentHashMap
Queue
ArrayBlockingQueue
LinkedBlockingQueue
ConcurrentLinkedQueue

爲節省空間對於非重點代碼不作展現

什麼是ArrayList

ArrayList底層是由數組組成的一種數據結構,能夠進行動態的增刪改查html

ArrayList用來幹嗎

ArrayList通常用於對數據的存儲java

源碼解析針對重要點

  1. 數據的存儲
  2. 數據的操做
  3. 何時擴容
  4. 是否線程安全

帶上問題去找答案數組

數據的存儲

從咱們使用ArrayList開始
new ArrayList<>();
public ArrayList() {
    super();
    this.elementData = EMPTY_ELEMENTDATA;
}
private static final Object[] EMPTY_ELEMENTDATA = {};
private transient Object[] elementData;
elementData爲儲存數據所用容器,經過默認構造方法建立的ArrayList容器爲空

數據的操做

添加

public boolean add(E e) {
	ensureCapacityInternal(size + 1);  
	elementData[size++] = e;//size表示當前使用下表,直接將元素放入到elementData的指定位置
	return true;
}
指定位置的添加,設計到元素的後移
 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++;
}
private void rangeCheckForAdd(int index) {
	if (index > size || index < 0)
		throw new IndexOutOfBoundsException(outOfBoundsMsg(index));//很常見的異常
}

刪除

public E remove(int index) {
	rangeCheck(index);
	checkForComodification();
	E result = parent.remove(parentOffset + index);
	this.modCount = parent.modCount;
	this.size--;
	return result;
}
final void checkForComodification() {
	if (modCount != expectedModCount)//針對在對於list進行遍歷時進行其餘操做,modCount會改變,而expectedModCount值在listInterator時給定的會拋出異常
		throw new ConcurrentModificationException();
}

查詢

public E get(int index) {
    rangeCheck(index);//這就沒啥好多的了
    return elementData(index);
}

何時擴容

public boolean add(E e) {
	ensureCapacityInternal(size + 1);  // Increments modCount!!
	elementData[size++] = e;
	return true;
}
private void ensureCapacityInternal(int minCapacity) {
	if (elementData == EMPTY_ELEMENTDATA) { //若是容器爲空,初始化容器,兩個值中取最大值
		minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
	}
	ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
	modCount++;//操做次數+1
	// overflow-conscious code
	if (minCapacity - elementData.length > 0) //擴容,當前元素數量已經等於容器了須要進行擴容
		grow(minCapacity);
}
private void grow(int minCapacity) {
	// overflow-conscious code
	int oldCapacity = elementData.length;//當前容器大小
	int newCapacity = oldCapacity + (oldCapacity >> 1);// oldCapacity + oldCapacity*0.5
	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);//擴容
}
private static int hugeCapacity(int minCapacity) {
	if (minCapacity < 0) // overflow //若是爲負數拋出內存溢出錯誤
		throw new OutOfMemoryError();
	return (minCapacity > MAX_ARRAY_SIZE) ? //尚未爲負數,那麼元素最大大小改成int的最大值,注意MAX_ARRAY_SIZE爲最大值-8
		Integer.MAX_VALUE :
		MAX_ARRAY_SIZE;
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

是否線程安全

ArrayList不是線程安全的安全

以添加爲例
elementData[size++] = e;
一個 ArrayList ,在添加一個元素的時候,它可能會有兩步來完成: 
1. 在 Items[Size] 的位置存放此元素; 
2. 增大 Size 的值。 
在單線程運行的狀況下,若是 Size = 0,添加一個元素後,此元素在位置 0,並且 Size=1; 
而若是是在多線程狀況下,好比有兩個線程,線程 A 先將元素存放在位置 0。可是此時 CPU 調度線程A暫停,線程 B 獲得運行的機會。線程B也向此 ArrayList 添加元素,由於此時 Size 仍然等於 0 (注意哦,咱們假設的是添加一個元素是要兩個步驟哦,而線程A僅僅完成了步驟1),因此線程B也將元素存放在位置0。而後線程A和線程B都繼續運行,都增長 Size 的值。 
那好,如今咱們來看看 ArrayList 的狀況,元素實際上只有一個,存放在位置 0,而 Size 卻等於 2。這就是「線程不安全」了。

使用ArrayList的注意事項

  1. arrayList不是線程安全的,不要有多個線程同時操做一個arrayList
  2. 不要在循環中對arrayList作其餘操做,會引起異常
  3. 針對已經肯定大小的List請直接傳入參數,避免屢次擴容

參考

相關文章
相關標籤/搜索