前言:
你好,早上、中午、下午、晚上好。我是辛巴哥。一名無緣985,日常996工程師。
今天我辛巴哥來教娜娜學ArrayList
開始快速
import java.util.ArrayList;
/****
* 求關注 微信搜:Java大型網站架構
*/
public class ArrayListDemo {
public static void main(String[] args) {
ArrayList<String> arrayList= new ArrayList<String>();
arrayList.add("辛巴");
arrayList.add("娜娜");
arrayList.add("微信搜:Java大型網站架構");
for (String s:arrayList){
System.out.println(s);
}
}
}
大家在日常開發中都是這樣使用的,只要是java開發人員,無論你是1年工作還是10年工作。我相信Arraylist都是非常熟悉使用的,但是知道其底層實現的人就少很多了,這發生在我身邊的事情,小編我現在在某大廠上班,經常面試一些工作5-6年的程序員,問問他們HashMap和ArrayList底層實現,都知道是數組、鏈表、hash算法,但是細問下就露餡了,所以今天有必要和大家分享ArrayList底層實現。當然Hashmap的底層實現我也講過,可以看我之前的文章。
技術本質
很多老鐵都知道ArrayList底層是用數組爲存儲結構的。我們先來熟悉下數組
數組
數組:採用一段連續的存儲單元來存儲數據。對於指定下標的查找,時間複雜度爲O(1);對於一般的插入刪除操作,
涉及到數組元素的移動,其平均複雜度也爲O(n)
Java代碼表示
//數組:採用一段連續的存儲單元來存儲數據。
//特點:指定下標O(1) 刪除插入O(N) 數組:查詢快 插入慢 ArrayList
public static void main(String[] args) {
Integer[] integers = new Integer[10];
integers[0]=1;
integers[1]=2;
integers[2]=3;
integers[9]=4;
插入原理
我們熟悉了數組的特性之後,我們回到剛我們快速入門的代碼 ,我們add三個字符串是怎麼插入到我們數組裏面去的了, 是怎麼插(吃啊插)入進去的了?我們來看下源碼:
arrayList.add(“辛巴”);
arrayList.add(“娜娜”);
arrayList.add(“微信搜:Java大型網站架構”);
通過上述代碼我們可以確定泛型e插入到elementData數組裏 並且size++。ensureCapacityInternal方法我們先放放等會說。
我們先說說怎麼插入到數組中,從0開始按順序size++插入到數組中分別爲:
如果有一天插入的數據不數組長度還多了怎麼辦?
擴容
剛纔在add方法有ensureCapacityInternal方法還沒說,這個方法我們來看下源碼。
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
源碼意思:計算最小容量,DEFAULTCAPACITY_EMPTY_ELEMENTDATA(默認容量)是DEFAULTCAPACITY_EMPTY_ELEMENTDATA=10
算出最小容量在看下:
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
傳入最小容量值,修改+1。
minCapacity - elementData.length > 0判斷需要的容量與默認容量的大小,差爲負值是不需要擴容的。如果大於0說正數就需要擴容調用grow方法
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);
}
源碼意思:數組擴容
第一個if條件是:
首先把數組的長度賦給老的容量,也就是10,然後新的容量newCapacity=老的容量(10)+老的容量右移一位(5),
第二個if條件是:
如果新容量-(size+1)爲負數,把size+1賦給新容量。如果新容量比數組的最大擴容量都大,會報異常,或者把最大的賦給它。如果都不是,就把新容量拷貝給數組,擴容完成。
對應圖如下:
刪除原理
我們知道add插入的原理,刪除其實也不難了吧。大致意思就是通過下找到數組對應的對象讓其爲null,但是我們之前插入時擴容的數組怎麼處理了?我們現在不需要這麼多數據了呀?
下標位置刪除:
public E remove(int index) {
rangeCheck(index);//檢查index這個值是否有在size裏面,保證其正確合法性
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;/ /將index+1以及之後的元素向前移動一位,覆蓋被刪除的值
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // 該對應下表值賦值爲null。
return oldValue;
}
對象元素刪除:
指定元素刪除:首先判斷是否爲空,空的時候直接刪除
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;
}
}
//如果沒有匹配元素,返回false
return false;
}
快速刪除:不需要檢測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
}
總結:
現在知道爲什麼數組特性:插入刪除慢,查詢快(還沒說明)
原因是插入有可能需要擴容。
刪除需要重新分配數組中數據元素。比如圖中刪除“娜娜”
娜娜下標爲1,凡是在下標爲1之後的元素都需要往前挪一個位置,
這是件很可怕的事情。因爲刪除的元素越靠前,那就意味着需要挪動的數據就越多。因此數組插入和刪除會變慢的原因在於此了明白的牛人點個贊或者回復下吧。
好了各位,以上就是這篇文章的全部內容了,能看到這裏的人呀,都是牛人。
白嫖不好,創作不易,各位的支持和認可,就是我創作的最大動力,我們下篇文章見!
微信搜索:Java大型網站架構