java list addall源码_Java集合:ArrayList源码分析
其實(shí)我看到已有很多大佬寫(xiě)過(guò)此類(lèi)文章,并且寫(xiě)的也比較清晰明了,那我為何要再寫(xiě)一遍呢?其實(shí)也是為了加深本身的印象,鞏固本身的基礎(chǔ)html
(主要是不少文章沒(méi)有寫(xiě)出來(lái)我想知道的東西!!!?!!!!)java
前言
我說(shuō)一下我認(rèn)為怎么樣才能去看懂,看透徹一個(gè)源碼。
在你去分析源碼的時(shí)候,首先要會(huì)用,要明白這個(gè)工具類(lèi)的做用,若是連一個(gè)工具類(lèi)都包含哪些功能,這些功能的做用都不清楚,我以為看源碼就是一種煎熬。(固然,大佬除外)api
?自我洗腦中~我是大佬!我是大佬!我是大佬!(he~tui!我不配!!!)數(shù)組
正文
本次是基于JDK1.8來(lái)具體分析ArrayList源碼dom
ArrayList的概念:
動(dòng)態(tài)數(shù)組,它提供了動(dòng)態(tài)的增長(zhǎng)和減小元素,實(shí)現(xiàn)了Collection和List接口,靈活的設(shè)置數(shù)組的大小等好處。
每一個(gè) ArrayList 實(shí)例都有一個(gè)容量。該容量是指用來(lái)存儲(chǔ)列表元素的數(shù)組的大小。它老是至少等于列表的大小。隨著向 ArrayList 中不斷添加元素,其容量也自動(dòng)增加。函數(shù)
一、繼承結(jié)構(gòu)分析
咱們先來(lái)看一下ArrayList類(lèi)的繼承結(jié)構(gòu):工具
?
Java支持單繼承,多實(shí)現(xiàn)源碼分析
AbstractList:性能
抽象接口類(lèi),目的是使用抽象類(lèi)中已經(jīng)實(shí)現(xiàn)的方法。
咱們點(diǎn)開(kāi)AbstractList源碼,會(huì)看到其實(shí)AbstractList已經(jīng)也實(shí)現(xiàn)了List接口,為何要先繼承AbstractList,而讓AbstractList先實(shí)現(xiàn)List?而不是讓ArrayList直接實(shí)現(xiàn)List?優(yōu)化
這里是有一個(gè)思想,接口中全都是抽象的方法,而抽象類(lèi)中能夠有抽象方法,還能夠有具體的實(shí)現(xiàn)方法,正是利用了這一點(diǎn),讓AbstractList實(shí)現(xiàn)接口中一些通用的方法,而如ArrayList就繼承這個(gè)AbstractList類(lèi),拿到一些通用的方法,而后本身在實(shí)現(xiàn)一些本身特有的方法,這樣一來(lái),讓代碼更簡(jiǎn)潔,就繼承結(jié)構(gòu)最底層的類(lèi)中通用的方法都抽取出來(lái),先一塊兒實(shí)現(xiàn)了,減小重復(fù)代碼。因此通常看到一個(gè)類(lèi)上面還有一個(gè)抽象類(lèi),應(yīng)該就是這個(gè)做用。
List:
使用List的接口規(guī)范
RandomAccess:
這個(gè)是一個(gè)標(biāo)記性接口,經(jīng)過(guò)查看api文檔,它的做用就是用來(lái)快速隨機(jī)存取,有關(guān)效率的問(wèn)題,在實(shí)現(xiàn)了該接口的話,那么使用普通的for循環(huán)來(lái)遍歷,性能更高,例如arrayList。而沒(méi)有實(shí)現(xiàn)該接口的話,使用Iterator來(lái)迭代,這樣性能更高,例如linkedList。因此這個(gè)標(biāo)記性只是為了讓咱們知道咱們用什么樣的方式去獲取數(shù)據(jù)性能更好。
Cloneable:
Serializable:
實(shí)現(xiàn)該序列化接口,代表該類(lèi)能夠被序列化,什么是序列化?簡(jiǎn)單的說(shuō),就是可以從類(lèi)變成字節(jié)流傳輸,而后還能從字節(jié)流變成原來(lái)的類(lèi)。
🤔🤔🤔🤔為何AbstractList已經(jīng)實(shí)現(xiàn)了List,ArrayList還要再實(shí)現(xiàn)一次呢?
其實(shí)ArrayList再去實(shí)現(xiàn)一次List在這里并無(wú)什么實(shí)際意義,這實(shí)際上是一個(gè)錯(cuò)誤,由于做者寫(xiě)這代碼的時(shí)候以為這個(gè)會(huì)有用處,可是其實(shí)并沒(méi)什么用,但由于沒(méi)什么影響,就一直留到了如今。有興趣的同窗能夠去研究一下。
二、類(lèi)分析
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
/**
* 缺省容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 有參構(gòu)造缺省空數(shù)組
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 無(wú)參構(gòu)造缺省空數(shù)組
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 數(shù)組元素(實(shí)際操做的數(shù)組,新增,刪除等方法都是在此數(shù)組發(fā)生操做)
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* 實(shí)際數(shù)組的大小
*/
private int size;
/**
* 數(shù)組的最大容量
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
這里分析幾個(gè)地方:
(1)為何數(shù)組最大容量是Integer.MAX_VALUE - 8,而不是Integer.MAX_VALUE?
其實(shí)源碼中給了備注:意思應(yīng)該是有些虛擬機(jī)在數(shù)組中保留了一些頭信息。避免內(nèi)存溢出!
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
(2)為何定義了兩個(gè)空數(shù)組?
首先定義空數(shù)組的根本緣由是
優(yōu)化處理,若是一個(gè)應(yīng)用中有不少這樣ArrayList空實(shí)例的話,就會(huì)有不少的空數(shù)組,無(wú)疑是為了優(yōu)化性能,全部ArrayList空實(shí)例都指向同一個(gè)空數(shù)組。二者都是用來(lái)減小空數(shù)組的建立,全部空ArrayList都共享空數(shù)組。二者的區(qū)別主要是用來(lái)起區(qū)分做用,針對(duì)有參無(wú)參的構(gòu)造在擴(kuò)容時(shí)作區(qū)分走不一樣的擴(kuò)容邏輯,優(yōu)化性能。
(3)elementData為何定義成transient?
三、構(gòu)造方法
?
Array List總共有三個(gè)構(gòu)造方法,下面咱們一一分析
1)無(wú)參構(gòu)造方法 ArrayList()
/**
* 將空數(shù)組初始化大小為10(將空數(shù)組初始化大小為10,具體在何時(shí)初始化大小為10,待會(huì)兒會(huì)說(shuō)到)
*/
public ArrayList() {
// 將elementData元素?cái)?shù)組初始化為空數(shù)組
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
無(wú)參構(gòu)造方法中,將元素?cái)?shù)組elementData初始化為空數(shù)組。(注意:這里就體現(xiàn)了我上文說(shuō)到的,為何定義兩個(gè)空數(shù)組)
2)有參構(gòu)造方法 ArrayList(int)
/**
* 構(gòu)造一個(gè)具備指定初始容量的列表
*
* @param initialCapacity: 初始化數(shù)組的值
*/
public ArrayList(int initialCapacity) {
//若是初始化的值大于0,則給定elementData一個(gè)長(zhǎng)度為initialCapacity的數(shù)組
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) { // 若是初始化的值等于0,則初始化為空數(shù)組
this.elementData = EMPTY_ELEMENTDATA;
} else { //不然(小于0的狀況)拋出異常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
3)有參構(gòu)造方法 ArrayList(Collection extends E> c)
/**
* 構(gòu)造一個(gè)指定元素的集合(此方法不太經(jīng)常使用)
* @param c
*/
public ArrayList(Collection extends E> c) {
// 將集合轉(zhuǎn)換為數(shù)組并賦值給elementData
elementData = c.toArray();
// 若是集合的大小不為0
if ((size = elementData.length) != 0) {
// 若是轉(zhuǎn)換后的數(shù)組不是泛型(object),則須要用Arrays的工具轉(zhuǎn)換一下為object數(shù)組(這里再也不對(duì)Arrays.copyOf展開(kāi)論述)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else { // 不然初始化elementData為一個(gè)空數(shù)組
this.elementData = EMPTY_ELEMENTDATA;
}
}
對(duì)于當(dāng)前構(gòu)造方法,我舉個(gè)例子,更清晰明了
?
四、經(jīng)常使用方法源碼分析
boolean add(E e)
重中之重,ArrayList的核心奧秘!!!!
/**
* 在數(shù)組中增長(zhǎng)一個(gè)元素
* @param e 元素對(duì)象
*/
public boolean add(E e) {
// 肯定內(nèi)部容量是否夠用,size是元素?cái)?shù)組中數(shù)據(jù)的個(gè)數(shù),由于要添加一個(gè)元素,因此size+1,先判斷size+1的這個(gè)個(gè)數(shù)數(shù)組可否放得下,就在這個(gè)方法中去判斷是否數(shù)組.length是否夠用了。
ensureCapacityInternal(size + 1);
// 將元素e賦值到elementData末尾
elementData[size++] = e;
return true;
}
// 此方法能夠理解為中轉(zhuǎn)計(jì)算
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 判斷數(shù)組是否是空數(shù)組, 若是是空數(shù)組(此時(shí)minCapacity = 0 + 1 = 1),就將minCapacity初始化為10,但此時(shí)僅僅是返回要初始化數(shù)組的大小,并無(wú)真正初始化數(shù)組為10
// private static final int DEFAULT_CAPACITY = 10;
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// Math.max(參數(shù)1,參數(shù)2)方法的意思是返回參數(shù)中最大的數(shù),若是是空數(shù)組是,此時(shí)返回的是10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 若是初始化的集合不是空,則返回元素?cái)?shù)組的size + 1
return minCapacity;
}
// 別擔(dān)憂 我有奧妙全自動(dòng)(奧妙洗衣粉~全國(guó)人民都知道~~)
private void ensureExplicitCapacity(int minCapacity) {
// 結(jié)構(gòu)變化記錄+1 在父類(lèi)AbstractList中定義了一個(gè)int型的屬性:modCount,記錄了ArrayList結(jié)構(gòu)性變化的次數(shù)
modCount++;
// 判斷數(shù)組是否夠用,若是不夠用,則自動(dòng)擴(kuò)容
// 一、當(dāng)初始化的集合為空數(shù)組時(shí),此時(shí)minCapacity是10,而elementData的長(zhǎng)度為0,因此須要擴(kuò)容
// 二、當(dāng)初始化的集合不為空是,也就是給定了大小,或已經(jīng)初始化了元素,此時(shí)的minCapacity = 實(shí)際數(shù)組個(gè)數(shù)+1,此時(shí)判斷集合不夠用,也須要進(jìn)行擴(kuò)容,不然元素會(huì)溢出
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 自動(dòng)擴(kuò)容
private void grow(int minCapacity) {
// oldCapacity:元素?cái)?shù)組的實(shí)際長(zhǎng)度(即擴(kuò)充前的數(shù)組大小)
int oldCapacity = elementData.length;
// oldCapacity 擴(kuò)容1.5倍賦值給newCapacity( >>為右移運(yùn)算符,至關(guān)于除以2 即oldCapacity/2 )
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 若是初始化為空的狀況,則將數(shù)組擴(kuò)容為10,此時(shí)才是真正初始化元素?cái)?shù)組elementData大小為10
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 若是1.5倍的數(shù)組大小超過(guò)了集合的最大長(zhǎng)度,則調(diào)用hugeCapacity方法,從新計(jì)算,也就是給定最大的集合長(zhǎng)度
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 已經(jīng)肯定了大小,就將元素copy到elementData元素?cái)?shù)組中~~
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
// 內(nèi)存溢出判斷
if (minCapacity < 0)
throw new OutOfMemoryError();
// 這里的邏輯為:若是須要擴(kuò)容的大小比數(shù)組的最大值都大,就返回Integer,MAX_VALUE(int最大值),不然返回集合的最大值(int最大值-8)
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
void add(int index, E element)
增長(zhǎng)元素到指定下標(biāo)
/**
* 增長(zhǎng)元素到指定下標(biāo)
*
* @param index 下標(biāo)
* @param element 元素
*/
public void add(int index, E element) {
// 參數(shù)校驗(yàn)
rangeCheckForAdd(index);
// 此方法再也不贅述,參考上文Add方法重的論述
ensureCapacityInternal(size + 1);
// 請(qǐng)看下面代碼塊的注釋
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 將指定元素覆蓋到指定下標(biāo)
elementData[index] = element;
// 長(zhǎng)度size + 1
size++;
}
/**
* 適用于add 和 addAll的校驗(yàn)方法
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
System.arraycopy 方法解析
/**
* System提供了一個(gè)靜態(tài)方法arraycopy(),咱們可使用它來(lái)實(shí)現(xiàn)數(shù)組之間的復(fù)制
* 函數(shù)為:public static native void arraycopy(Object src,int srcPos,Object dest, int destPos,int length);
* @param src the source array. 源數(shù)組
* @param srcPos starting position in the source array. 源數(shù)組的起始位置
* @param dest the destination array. 目標(biāo)數(shù)組
* @param destPos starting position in the destination data. 目標(biāo)數(shù)組的起始位置
* @param length the number of array elements to be copied. 復(fù)制的長(zhǎng)度
//舉個(gè)例子
public static void main(String[] args){
int[] arr = {1,2,3,4,5};
int[] copied = new int[10];
System.out.println(Arrays.toString(copied));
System.arraycopy(arr, 0, copied, 1, 5);//5是復(fù)制的長(zhǎng)度
System.out.println(Arrays.toString(copied));
}
輸出結(jié)果為:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 2, 3, 4, 5, 0, 0, 0, 0]
boolean remove(Object o)
根據(jù)元素進(jìn)行刪除
public E remove(int index) {
// 參數(shù)校驗(yàn)
rangeCheck(index);
// 結(jié)構(gòu)變化記錄+1
modCount++;
// 獲取舊數(shù)據(jù),返回給開(kāi)發(fā)人員,目的是讓開(kāi)發(fā)人員知道刪除了哪一個(gè)數(shù)據(jù)
E oldValue = elementData(index);
// 計(jì)算須要元素須要移動(dòng)的次數(shù)
int numMoved = size - index - 1;
if (numMoved > 0)
// 同上文敘述
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 將最后一個(gè)元素置為空(元素前移,最后一位置為空),讓GC回收
elementData[--size] = null;
return oldValue;
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
void clear()
清空集合
/**
* 清空集合
*/
public void clear() {
modCount++;
// 將數(shù)組置為空,促使GC回收
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
E get(int index)
返回此列表中指定位置上的元素
/**
* 檢查給定的索引是否在范圍內(nèi)。 若是沒(méi)有,則拋出一個(gè)適當(dāng)?shù)倪\(yùn)行時(shí)異常。
* @param index : 下標(biāo)
*/
public E get(int index) {
// 校驗(yàn)下標(biāo)有效性
rangeCheck(index);
// 返回元素?cái)?shù)組中指定index位置的數(shù)據(jù)
return elementData(index);
}
private void rangeCheck(int index) {
// 若是下標(biāo)大于實(shí)際數(shù)組長(zhǎng)度(元素?cái)?shù)組最后一個(gè)數(shù)據(jù)下標(biāo)為size-1)
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E set(int index, E element)
/**
* 覆蓋相應(yīng)下標(biāo)的數(shù)據(jù)
* @param index 下標(biāo)
* @param element 元素?cái)?shù)據(jù)
*/
public E set(int index, E element) {
// 校驗(yàn)方法(再也不解釋,與get方法中同樣)
rangeCheck(index);
// 獲取到舊數(shù)據(jù),這里將舊數(shù)據(jù)返回出去,為了讓開(kāi)發(fā)者知道替換的是哪一個(gè)值
E oldValue = elementData(index);
// 將指定下標(biāo)覆蓋為新元素
elementData[index] = element;
return oldValue;
}
結(jié)尾
其實(shí)ArrayList中還有不少不少方法,這里就不在一一敘述了,由于你理解了本文中所說(shuō)的源碼,其實(shí)其它再去理解,再去查看,是比較容易簡(jiǎn)單的。
?
本篇文章就講到這里,若是有寫(xiě)的很差的地方,請(qǐng)多多指教,我是善良的小黑哥,但愿與你們共同進(jìn)步!!
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專(zhuān)家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的java list addall源码_Java集合:ArrayList源码分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ssd3 employee.java_S
- 下一篇: java事务过大影响系统性能吗_Java