摘要:源碼分析默認(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