對于集合的源碼分析发皿,一般我會采用這幾種方式
- 怎么添加元素?
- 怎么獲取元素拂蝎?
- 怎么刪除元素穴墅?
- 內(nèi)部數(shù)據(jù)結構實現(xiàn)?
話不多說,直接走起玄货。
一.怎么添加元素皇钞?
一般我們通過ArrayList添加元素。一般會調(diào)用其構造方法,然后調(diào)用其對象的add方法
查看空參構造函數(shù)
//Constructs an empty list with an initial capacity of ten.
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
通過構造函數(shù)可以發(fā)現(xiàn)松捉。ArrayList在調(diào)用無參的構造函數(shù)時夹界,會構造一個長度為10的緩存數(shù)組
查看add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
通過該方法發(fā)現(xiàn) ArralyList內(nèi)部的數(shù)據(jù)結構其實是一個數(shù)組(elementData[size++] = e;)并且在添加時會先判斷當前容器在添加了一個對象之后該對象的容納能力(主要為了,在下一次添加元素的時候隘世,緩存數(shù)組能夠有足夠的空間添加元素)可柿。之后將元素添加到數(shù)組末尾。
繼續(xù)查看ensureCapacityInternal()方法
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
這里我們發(fā)現(xiàn)丙者,如果當前elementData為空的話复斥,minCapacity=DEFAULT_CAPACITY,同時DEFAULT_CAPACITY的默認值是10,從這我們可以看出,在第一次初始化的時候械媒,ArrayList內(nèi)部會默認創(chuàng)建一個內(nèi)部長度為10的數(shù)組目锭。
繼續(xù)點擊ensureExplicitCapacity()方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//判斷添加元素后,緩存數(shù)組時候需要擴展
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
該方法會記錄當前數(shù)組的更改次數(shù)纷捞,并且判斷當前數(shù)組添加后痢虹,是否需要進行增長,
繼續(xù)走grow方法(重點的來了@夹濉J婪帧!)
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//擴展數(shù)組的長度缀辩,
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);
}
該方法會擴展緩存為當前數(shù)組的長度為 原數(shù)組長度+原數(shù)組長度的二分之一臭埋,也就是按照原數(shù)組的50%進行增長,同時該數(shù)組最大的擴展長度是Integer.MAX_VALUE - 8臀玄。也就是ArrayList最多能存儲的數(shù)據(jù)長度瓢阴,通過擴展數(shù)組長度以后,在下一次添加數(shù)據(jù)的時候健无,ArrayList就有足夠的空間去添加新的元素了荣恐。
二.怎么獲取元素
其實ArrayList獲取其中的元素很簡單,根據(jù)角標獲取對應數(shù)組中的元素累贤,具體代碼如下:
public E get(int index) {
if (index >= size)//判斷當前角標長度是否超過數(shù)組長度叠穆,如果是拋出異常,反之返回數(shù)據(jù)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
return (E) elementData[index];
}
三.怎么刪除元素
在ArrayList中臼膏,有兩個關于刪除元素的方法硼被,一個是remove(int),另一個是remove(Object)
1.remove(int)方法
public E remove(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // 將數(shù)組最后一位置null
return oldValue;
}
這里先判斷刪除角標是否超過數(shù)組長度,然后通過System.arrayCopty()方法將角標對應的元素刪除渗磅。
這里對System.arrayCopty()方法解釋一下嚷硫。該方法的第一個參數(shù)是源數(shù)組检访,第二個參數(shù)是復制的開始角標,第二個參數(shù)是目標數(shù)組仔掸。第三個參數(shù)是目標數(shù)組與源數(shù)組的復制數(shù)據(jù)開始角標脆贵。最后一個參數(shù)是復制的長度。(注意:F鹉骸B舭薄!復制的長度不能大于目標數(shù)組減去開始角標的長度或源數(shù)組減去開始角標的長度)
eg:
int[] a = {0, 1, 2, 3, 4};
int[] b = {5, 6, 7, 8, 9};
System.arraycopy(a, 0, b, 1, 3);
// 則進行操作后 b = {5鞋怀,0,1,2,9}
2.remove(Object)方法
public boolean remove(Object o) {
if (o == null) {//判斷當前元素是否為空双泪,遍歷數(shù)組,獲取其角標
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;
}
private void fastRemove(int index) {//根據(jù)角標密似,刪除相應元素
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
}
remove(Object)根據(jù)object在數(shù)組的角標,執(zhí)行fastRemove(index)方法葫盼。刪除方法與remoIndex(int)一樣残腌。這里就不在分析了。
總結
- ArrayList內(nèi)部實現(xiàn)是數(shù)組贫导,且當數(shù)組長度不夠時抛猫,數(shù)組的會進行原數(shù)組長度的1.5倍擴容。
- ArrayList內(nèi)部元素是可以重復的孩灯。且有序的闺金,因為是按照數(shù)組一個一個進行添加的。
- ArrayList是線程不安全的峰档,因為其內(nèi)部添加败匹、刪除、等操作讥巡,沒有進行同步操作掀亩。
- ArrayList增刪元素速度較慢,因為內(nèi)部實現(xiàn)是數(shù)組欢顷,每次操作都會對數(shù)組進行復制操作槽棍,復制操作是比較耗時的
最后,附上我寫的一個基于Kotlin 仿開眼的項目SimpleEyes(ps: 其實在我之前抬驴,已經(jīng)有很多小朋友開始仿這款應用了炼七,但是我覺得要做就做好。所以我的項目和其他的人應該不同布持,不僅僅是簡單的一個應用豌拙。但是,但是鳖链。但是姆蘸。重要的話說三遍墩莫。還在開發(fā)階段,不要打我)逞敷,歡迎大家follow和start