摘要:源碼分析默認(rèn)容量默認(rèn)容量為,也就是通過創(chuàng)建時(shí)的默認(rèn)容量。集合中元素的個(gè)數(shù)真正存儲(chǔ)元素的個(gè)數(shù),而不是數(shù)組的長度。方法刪除指定元素值的元素,時(shí)間復(fù)雜度為。方法求兩個(gè)集合的交集。
簡介
ArrayList是一種以數(shù)組實(shí)現(xiàn)的List,與數(shù)組相比,它具有動(dòng)態(tài)擴(kuò)展的能力,因此也可稱之為動(dòng)態(tài)數(shù)組。
繼承體系ArrayList實(shí)現(xiàn)了List, RandomAccess, Cloneable, java.io.Serializable等接口。
ArrayList實(shí)現(xiàn)了List,提供了基礎(chǔ)的添加、刪除、遍歷等操作。
ArrayList實(shí)現(xiàn)了RandomAccess,提供了隨機(jī)訪問的能力。
ArrayList實(shí)現(xiàn)了Cloneable,可以被克隆。
ArrayList實(shí)現(xiàn)了Serializable,可以被序列化。
源碼分析/**
* 默認(rèn)容量, 默認(rèn)容量為10,也就是通過new ArrayList()創(chuàng)建時(shí)的默認(rèn)容量。
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 空數(shù)組,如果傳入的容量為0時(shí)使用, 通過new ArrayList(0)創(chuàng)建時(shí)用的是這個(gè)空數(shù)組。
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 空數(shù)組,傳傳入容量時(shí)使用,添加第一個(gè)元素的時(shí)候會(huì)重新初始為默認(rèn)容量大小
* 這種是通過new ArrayList()創(chuàng)建時(shí)用的是這個(gè)空數(shù)組,
* 與EMPTY_ELEMENTDATA的區(qū)別是在添加第一個(gè)元素時(shí)使用這個(gè)空數(shù)組的會(huì)初始化為DEFAULT_CAPACITY(10)個(gè)元素。
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 存儲(chǔ)元素的數(shù)組
* 真正存放元素的地方,使用transient是為了不序列化這個(gè)字段。
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* 集合中元素的個(gè)數(shù)
* 真正存儲(chǔ)元素的個(gè)數(shù),而不是elementData數(shù)組的長度。
*/
private int size;
ArrayList(int initialCapacity)構(gòu)造方法
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
// 如果傳入的初始容量大于0,就新建一個(gè)數(shù)組存儲(chǔ)元素
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 如果傳入的初始容量等于0,使用空數(shù)組EMPTY_ELEMENTDATA
this.elementData = EMPTY_ELEMENTDATA;
} else {
// 如果傳入的初始容量小于0,拋出異常
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
}
}
ArrayList()構(gòu)造方法
public ArrayList() {
// 如果沒有傳入初始容量,則使用空數(shù)組DEFAULTCAPACITY_EMPTY_ELEMENTDATA
// 使用這個(gè)數(shù)組是在添加第一個(gè)元素的時(shí)候會(huì)擴(kuò)容到默認(rèn)大小10
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
ArrayList 構(gòu)造方法
/**
* 把傳入集合的元素初始化到ArrayList中
*/
public ArrayList(Collection extends E> c) {
// 集合轉(zhuǎn)數(shù)組
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// 檢查c.toArray()返回的是不是Object[]類型,如果不是,重新拷貝成Object[].class類型
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 如果c的空集合,則初始化為空數(shù)組EMPTY_ELEMENTDATA
this.elementData = EMPTY_ELEMENTDATA;
}
}
這里 c.toArray(); 因?yàn)榉祷氐挠锌赡懿皇荗bject[]類型,請(qǐng)看下面的代碼:
class MyList extends ArrayListadd(E e)方法{ /** * 子類重寫父類的方法,返回值可以不一樣 * 但這里只能用數(shù)組類型,換成Object就不行 * 應(yīng)該算是java本身的bug */ @Override public String[] toArray() { // 為了方便舉例直接寫死 return new String[]{"1", "2", "3"}; } }
添加元素到末尾,平均時(shí)間復(fù)雜度為O(1)。
public boolean add(E e) {
// 檢查是否需要擴(kuò)容
ensureCapacityInternal(size + 1);
// 把元素插入到最后一位
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 如果是空數(shù)組DEFAULTCAPACITY_EMPTY_ELEMENTDATA,就初始化為默認(rèn)大小10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
// 擴(kuò)容
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 新容量為舊容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果新容量發(fā)現(xiàn)比需要的容量還小,則以需要的容量為準(zhǔn)
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果新容量已經(jīng)超過最大容量了,則使用最大容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 以新容量拷貝出來一個(gè)新數(shù)組
elementData = Arrays.copyOf(elementData, newCapacity);
}
檢查是否需要擴(kuò)容;
如果elementData等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA則初始化容量大小為DEFAULT_CAPACITY;
新容量是老容量的1.5倍(oldCapacity + (oldCapacity >> 1)),如果加了這么多容量發(fā)現(xiàn)比需要的容量還小,則以需要的容量為準(zhǔn);
創(chuàng)建新容量的數(shù)組并把老數(shù)組拷貝到新數(shù)組;
add(int index, E element)方法添加元素到指定位置,平均時(shí)間復(fù)雜度為O(n)。
public void add(int index, E element) {
// 檢查是否越界
rangeCheckForAdd(index);
// 檢查是否需要擴(kuò)容
ensureCapacityInternal(size + 1);
// 將inex及其之后的元素往后挪一位,則index位置處就空出來了
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 將元素插入到index的位置
elementData[index] = element;
// 大小增1
size++;
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
檢查索引是否越界;
檢查是否需要擴(kuò)容;
把插入索引位置后的元素都往后挪一位;
在插入索引位置放置插入的元素;
大小加1;
addAll 方法求兩個(gè)集合的并集。
/**
* 將集合c中所有元素添加到當(dāng)前ArrayList中
*/
public boolean addAll(Collection extends E> c) {
// 將集合c轉(zhuǎn)為數(shù)組
Object[] a = c.toArray();
int numNew = a.length;
// 檢查是否需要擴(kuò)容
ensureCapacityInternal(size + numNew);
// 將c中元素全部拷貝到數(shù)組的最后
System.arraycopy(a, 0, elementData, size, numNew);
// 大小增加c的大小
size += numNew;
// 如果c不為空就返回true,否則返回false
return numNew != 0;
}
get(int index)方法
獲取指定索引位置的元素,時(shí)間復(fù)雜度為O(1)。
public E get(int index) {
// 檢查是否越界
rangeCheck(index);
// 返回?cái)?shù)組index位置的元素
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];
}
檢查索引是否越界,這里只檢查是否越上界,如果越上界拋出IndexOutOfBoundsException異常,如果越下界拋出的是ArrayIndexOutOfBoundsException異常。
返回索引位置處的元素;
remove(int index)方法刪除指定索引位置的元素,時(shí)間復(fù)雜度為O(n)。
public E remove(int index) {
// 檢查是否越界
rangeCheck(index);
modCount++;
// 獲取index位置的元素
E oldValue = elementData(index);
// 如果index不是最后一位,則將index之后的元素往前挪一位
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
// 將最后一個(gè)元素刪除,幫助GC
elementData[--size] = null; // clear to let GC do its work
// 返回舊值
return oldValue;
}
檢查索引是否越界;
獲取指定索引位置的元素;
如果刪除的不是最后一位,則其它元素往前移一位;
將最后一位置為null,方便GC回收;
返回刪除的元素。
可以看到,ArrayList刪除元素的時(shí)候并沒有縮容。
remove(Object o)方法刪除指定元素值的元素,時(shí)間復(fù)雜度為O(n)。
public boolean remove(Object o) {
if (o == null) {
// 遍歷整個(gè)數(shù)組,找到元素第一次出現(xiàn)的位置,并將其快速刪除
for (int index = 0; index < size; index++)
// 如果要?jiǎng)h除的元素為null,則以null進(jìn)行比較,使用==
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
// 遍歷整個(gè)數(shù)組,找到元素第一次出現(xiàn)的位置,并將其快速刪除
for (int index = 0; index < size; index++)
// 如果要?jiǎng)h除的元素不為null,則進(jìn)行比較,使用equals()方法
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
// 少了一個(gè)越界的檢查
modCount++;
// 如果index不是最后一位,則將index之后的元素往前挪一位
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
// 將最后一個(gè)元素刪除,幫助GC
elementData[--size] = null; // clear to let GC do its work
}
找到第一個(gè)等于指定元素值的元素;
快速刪除;
fastRemove(int index)相對(duì)于remove(int index)少了檢查索引越界的操作,可見jdk將性能優(yōu)化到極致。
retainAll方法求兩個(gè)集合的交集。
public boolean retainAll(Collection> c) {
// 集合c不能為null
Objects.requireNonNull(c);
// 調(diào)用批量刪除方法,這時(shí)complement傳入true,表示刪除不包含在c中的元素
return batchRemove(c, true);
}
/**
* 批量刪除元素
* complement為true表示刪除c中不包含的元素
* complement為false表示刪除c中包含的元素
*/
private boolean batchRemove(Collection> c, boolean complement) {
final Object[] elementData = this.elementData;
// 使用讀寫兩個(gè)指針同時(shí)遍歷數(shù)組
// 讀指針每次自增1,寫指針放入元素的時(shí)候才加1
// 這樣不需要額外的空間,只需要在原有的數(shù)組上操作就可以了
int r = 0, w = 0;
boolean modified = false;
try {
// 遍歷整個(gè)數(shù)組,如果c中包含該元素,則把該元素放到寫指針的位置(以complement為準(zhǔn))
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// 正常來說r最后是等于size的,除非c.contains()拋出了異常
if (r != size) {
// 如果c.contains()拋出了異常,則把未讀的元素都拷貝到寫指針之后
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// 將寫指針之后的元素置為空,幫助GC
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
// 新大小等于寫指針的位置(因?yàn)槊繉懸淮螌懼羔樉图?,所以新大小正好等于寫指針的位置)
size = w;
modified = true;
}
}
// 有修改返回true
return modified;
}
遍歷elementData數(shù)組;
如果元素在c中,則把這個(gè)元素添加到elementData數(shù)組的w位置并將w位置往后移一位;
遍歷完之后,w之前的元素都是兩者共有的,w之后(包含)的元素不是兩者共有的;
將w之后(包含)的元素置為null,方便GC回收;
removeAll求兩個(gè)集合的單方向差集,只保留當(dāng)前集合中不在c中的元素,不保留在c中不在當(dāng)前集體中的元素。
public boolean removeAll(Collection> c) {
// 集合c不能為空
Objects.requireNonNull(c);
// 同樣調(diào)用批量刪除方法,這時(shí)complement傳入false,表示刪除包含在c中的元素
return batchRemove(c, false);
}
總結(jié)
ArrayList內(nèi)部使用數(shù)組存儲(chǔ)元素,當(dāng)數(shù)組長度不夠時(shí)進(jìn)行擴(kuò)容,每次加一半的空間,ArrayList不會(huì)進(jìn)行縮容;
ArrayList支持隨機(jī)訪問,通過索引訪問元素極快,時(shí)間復(fù)雜度為O(1);
ArrayList添加元素到尾部極快,平均時(shí)間復(fù)雜度為O(1);
ArrayList添加元素到中間比較慢,因?yàn)橐嵋圃?,平均時(shí)間復(fù)雜度為O(n);
ArrayList從尾部刪除元素極快,時(shí)間復(fù)雜度為O(1);
ArrayList從中間刪除元素比較慢,因?yàn)橐嵋圃?,平均時(shí)間復(fù)雜度為O(n);
ArrayList支持求并集,調(diào)用addAll(Collection extends E> c)方法即可;
ArrayList支持求交集,調(diào)用retainAll(Collection extends E> c)方法即可;
ArrayList支持求單向差集,調(diào)用removeAll(Collection extends E> c)方法即可;
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/75971.html
摘要:加載因子是哈希表在其容量自動(dòng)增加之前可以達(dá)到多滿的一種尺度。當(dāng)哈希表中的條目數(shù)超出了加載因子與當(dāng)前容量的乘積時(shí),則要對(duì)該哈希表進(jìn)行操作即重建內(nèi)部數(shù)據(jù)結(jié)構(gòu),從而哈希表將具有大約兩倍的桶數(shù)。成員變量每個(gè)對(duì)由封裝,存在了對(duì)象數(shù)組中。 雖是讀書筆記,但是如轉(zhuǎn)載請(qǐng)注明出處 http://segmentfault.com/blog/exploring/ .. 拒絕伸手復(fù)制黨 LinkedLis...
摘要:簡介是的線程安全版本,內(nèi)部也是通過數(shù)組實(shí)現(xiàn),每次對(duì)數(shù)組的修改都完全拷貝一份新的數(shù)組來修改,修改完了再替換掉老數(shù)組,這樣保證了只阻塞寫操作,不阻塞讀操作,實(shí)現(xiàn)讀寫分離。 簡介 CopyOnWriteArrayList是ArrayList的線程安全版本,內(nèi)部也是通過數(shù)組實(shí)現(xiàn),每次對(duì)數(shù)組的修改都完全拷貝一份新的數(shù)組來修改,修改完了再替換掉老數(shù)組,這樣保證了只阻塞寫操作,不阻塞讀操作,實(shí)現(xiàn)讀寫...
摘要:是棧,它繼承于。滿二叉樹除了葉結(jié)點(diǎn)外每一個(gè)結(jié)點(diǎn)都有左右子葉且葉子結(jié)點(diǎn)都處在最底層的二叉樹。沒有鍵值相等的節(jié)點(diǎn)。這是數(shù)據(jù)庫選用樹的最主要原因。 在我們學(xué)習(xí)Java的時(shí)候,很多人會(huì)面臨我不知道繼續(xù)學(xué)什么或者面試會(huì)問什么的尷尬情況(我本人之前就很迷茫)。所以,我決定通過這個(gè)開源平臺(tái)來幫助一些有需要的人,通過下面的內(nèi)容,你會(huì)掌握系統(tǒng)的Java學(xué)習(xí)以及面試的相關(guān)知識(shí)。本來是想通過Gitbook的...
摘要:常用集合使用場景分析過年前的最后一篇,本章通過介紹,,,底層實(shí)現(xiàn)原理和四個(gè)集合的區(qū)別。和都是線程安全的,不同的是前者使用類,后者使用關(guān)鍵字。面試官會(huì)認(rèn)為你是一個(gè)基礎(chǔ)扎實(shí),內(nèi)功深厚的人才到這里常用集合使用場景分析就結(jié)束了。 Java 常用List集合使用場景分析 過年前的最后一篇,本章通過介紹ArrayList,LinkedList,Vector,CopyOnWriteArrayList...
閱讀 4042·2021-11-22 13:53
閱讀 1785·2021-08-25 09:39
閱讀 2496·2019-08-29 18:36
閱讀 1549·2019-08-26 13:35
閱讀 1281·2019-08-26 11:57
閱讀 1769·2019-08-23 15:57
閱讀 882·2019-08-23 14:55
閱讀 1226·2019-08-23 14:51