摘要:顯然,開發(fā)人員認(rèn)為通過下標(biāo)遍歷的數(shù)組結(jié)構(gòu)更加高效。在進(jìn)行分裂時(shí),原始保留中部至末尾的元素,新的保留原起始位置到中部的元素。同樣,也會(huì)進(jìn)行重新賦值,使得在使用前與保持一致。在遍歷時(shí),會(huì)調(diào)用方法,將動(dòng)作施加于元素之上。
java.util.ArrayList
ArrayList繼承自AbstractList,AbstractList為隨機(jī)訪問數(shù)據(jù)的結(jié)構(gòu),如數(shù)組提供了基本實(shí)現(xiàn),并且提供了Iterator。首先看AbstractList實(shí)現(xiàn)了什么方法。
AbstractList AbstractList里可以存儲(chǔ)null嗎null可以作為一項(xiàng)存儲(chǔ)在ArrayList中。ListIterator是遍歷訪問AbstractList中元素較好的方式,需要注意獲取元素序號(hào)的方法是previousIndex,而不是nextIndex,因?yàn)橹坝衝ext方法的調(diào)用。還有,這里判斷兩個(gè)元素是否相等采用的是equals方法,而不是==。
public int indexOf(Object o) { ListIteratorListIterator可以逆序訪問元素it = listIterator(); if (o==null) { while (it.hasNext()) if (it.next()==null) return it.previousIndex(); } else { while (it.hasNext()) if (o.equals(it.next())) return it.previousIndex(); } return -1; }
從后向前遍歷AbstractList,過程恰恰相反。
public int lastIndexOf(Object o) { ListIteratorIterator遍歷元素時(shí),被其它線程修改了怎么辦it = listIterator(size()); if (o==null) { while (it.hasPrevious()) if (it.previous()==null) return it.nextIndex(); } else { while (it.hasPrevious()) if (o.equals(it.previous())) return it.nextIndex(); } return -1; }
如果在使用Iterator時(shí),有其他線程嘗試去修改List的大小,會(huì)被發(fā)現(xiàn)且立即拋出異常。
可以看class Itr implements Iterator
有兩個(gè)游標(biāo)分別記錄當(dāng)前指向的位置和上一次指向的位置。
/** * Index of element to be returned by subsequent call to next. */ int cursor = 0; /** * Index of element returned by most recent call to next or * previous. Reset to -1 if this element is deleted by a call * to remove. */ int lastRet = -1;如何處理遍歷過程中,刪除list中的元素導(dǎo)致的序號(hào)偏移
Iterator可以正確的刪除AbstractList中的元素,并且保證訪問的順序的正確性。
public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { AbstractList.this.remove(lastRet); if (lastRet < cursor) cursor--; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } }
刪除的元素是上一個(gè)next調(diào)用后的元素,并且連續(xù)調(diào)用兩次remove會(huì)拋出異常,因?yàn)樵刂荒軇h除一次,上次指向的元素已經(jīng)沒有了。
ListIterator可以添加元素在class ListItr extends Itr implements ListIterator
public void add(E e) { checkForComodification(); try { int i = cursor; AbstractList.this.add(i, e); lastRet = -1; cursor = i + 1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }ArrayList ArrayList用什么來(lái)存儲(chǔ)元素
就是用了一個(gè)簡(jiǎn)單的數(shù)組。。。
/** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */ transient Object[] elementData; // non-private to simplify nested class access如何節(jié)約ArrayList占用的空間
使用trimToSize函數(shù),將原始數(shù)組copy到適合大小的數(shù)組中。
/** * Trims the capacity of this ArrayList instance to be the * list"s current size. An application can use this operation to minimize * the storage of an ArrayList instance. */ public void trimToSize() { modCount++; if (size < elementData.length) { elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }用Iterator遍歷ArrayList真的高效嗎
不可否認(rèn),Iterator為遍歷一個(gè)集合提供了統(tǒng)一的接口,用戶可以忽略集合內(nèi)部的具體實(shí)現(xiàn),但是過多的封裝會(huì)導(dǎo)致效率的降低。顯然,Java開發(fā)人員認(rèn)為通過下標(biāo)遍歷ArrayList的數(shù)組結(jié)構(gòu)更加高效。所以重寫了indexOf和lastIndexOf。
public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1; }ArrayList的clone是淺copy
ArrayList只是新建了數(shù)組,copy了元素的引用,元素本身沒有進(jìn)行copy。clone方法為什么不new一個(gè)ArrayList對(duì)象,而是調(diào)用了Object類的clone?因?yàn)镺bject的clone函數(shù)是native的,更高效。
/** * Returns a shallow copy of this ArrayList instance. (The * elements themselves are not copied.) * * @return a clone of this ArrayList instance */ public Object clone() { try { //使用Object的clone獲得一個(gè)對(duì)象 ArrayList> v = (ArrayList>) super.clone(); v.elementData = Arrays.copyOf(elementData, size); v.modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn"t happen, since we are Cloneable throw new InternalError(e); } }ArrayList存儲(chǔ)空間的擴(kuò)展策略
如果list初始沒有元素,使用一個(gè)靜態(tài)的空數(shù)組。元素增加時(shí),空間擴(kuò)展為默認(rèn)的10。在后續(xù)的擴(kuò)展過程中,容量會(huì)每次增加原始大小的1/2。
ArrayList實(shí)現(xiàn)了自己的iterator雖然AbstractList實(shí)現(xiàn)了iterator,但ArrayList似乎不太滿意,又重新實(shí)現(xiàn)了一遍。主要區(qū)別就是在獲取元素時(shí),利用了數(shù)組結(jié)構(gòu)的優(yōu)勢(shì),可以直接通過下標(biāo)獲取元素,而不必通過調(diào)用方法。
用Spliterator分割遍歷ArrayListArrayList理所當(dāng)然的實(shí)現(xiàn)了自己的Spliterator,也就是ArrayListSpliterator。分割的策略簡(jiǎn)而言之為:二分+延遲初始化。
ArrayListSpliterator有如下屬性:
this.list = list; // OK if null unless traversed 保存目標(biāo)list this.index = origin; //起始位置 this.fence = fence; //終止位置 this.expectedModCount = expectedModCount; //期望修改次數(shù),用來(lái)判斷運(yùn)行時(shí)是否有其它線程修改
每次從中間開始分裂。在進(jìn)行分裂時(shí),原始spliterator保留中部至末尾的元素,新的spliterator保留原起始位置到中部的元素。
public ArrayListSpliteratortrySplit() { int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; return (lo >= mid) ? null : // divide range in half unless too small new ArrayListSpliterator (list, lo, index = mid, expectedModCount); }
在第一次創(chuàng)建spliterator時(shí),fence被初始為-1,所以在實(shí)際使用spliterator時(shí),getFence才會(huì)被調(diào)用,從而fence才會(huì)被賦值為數(shù)組大小。同樣,expectedModCount也會(huì)進(jìn)行重新賦值,使得spliterator在使用前與list保持一致。
private int getFence() { // initialize fence to size on first use int hi; // (a specialized variant appears in method forEach) ArrayListlst; if ((hi = fence) < 0) { if ((lst = list) == null) hi = fence = 0; else { expectedModCount = lst.modCount; hi = fence = lst.size; } } return hi; }
在遍歷list時(shí),會(huì)調(diào)用方法tryAdvance,將動(dòng)作施加于元素之上。該方法會(huì)檢查是否有其它線程的修改,在ArrayList中,有大量方法都會(huì)使用modCount來(lái)記錄修改,或者判斷是否在方法執(zhí)行時(shí)有其它線程的修改,目前看來(lái),用這個(gè)方法來(lái)檢測(cè)是否有并行修改使得list結(jié)構(gòu)變化是有效的,可以避免并行修改帶來(lái)的一些問題。
public boolean tryAdvance(Consumer super E> action) { if (action == null) throw new NullPointerException(); int hi = getFence(), i = index; if (i < hi) { index = i + 1; @SuppressWarnings("unchecked") E e = (E)list.elementData[i]; action.accept(e); if (list.modCount != expectedModCount) throw new ConcurrentModificationException(); return true; } return false; }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/67747.html
摘要:不同點(diǎn)是線程安全的,方法有關(guān)鍵字修飾。容量增長(zhǎng)策略默認(rèn)的增長(zhǎng)策略是每次在原容量的基礎(chǔ)上。的怎么做到線程安全的實(shí)現(xiàn)了自己的,為了保證并發(fā)線程安全的共享一個(gè),開發(fā)者在等方法中也加入了。與類繼承自,的實(shí)現(xiàn)不止一種方式,比如。 java.util.Vector Vector與ArrayList的異同 相同點(diǎn):隨機(jī)存取,可通過位置序號(hào)直接獲取數(shù)據(jù)。都是通過一個(gè)數(shù)組來(lái)存放元素。 不同點(diǎn):Vecto...
摘要:在中引入該方法,用來(lái)替換中的每個(gè)元素。的方法,只能在或之后使用,也就是確保當(dāng)前指向了一個(gè)存在的項(xiàng)。這里使用了,該方法可以將非對(duì)象先映射為型,然后進(jìn)行比較。存儲(chǔ)的是有序的數(shù)組,所以劃分時(shí)會(huì)按照順序進(jìn)行劃分。 java.util.List replaceAll 在Java8中引入該方法,用來(lái)替換list中的每個(gè)元素。 default void replaceAll(UnaryOpe...
摘要:說到復(fù)盤基礎(chǔ),并不是所有的都會(huì)復(fù)盤,沒那個(gè)時(shí)間更沒那個(gè)必要。比如,一些基礎(chǔ)的語(yǔ)法以及條件語(yǔ)句,極度簡(jiǎn)單。思前想后,我覺得整個(gè)計(jì)劃應(yīng)該從集合開始,而復(fù)盤的方式就是讀源碼。通常,隊(duì)列不允許隨機(jī)訪問隊(duì)列中的元素。 ?showImg(https://segmentfault.com/img/remote/1460000020029737?w=1080&h=711); 老讀者都知道,我是自學(xué)轉(zhuǎn)行...
摘要:集合類關(guān)系是和的父接口。相等必須是對(duì)稱的,約定只能和其它相等,亦然。和接口在中引入,這個(gè)單詞是和的合成,用來(lái)分割集合以給并行處理提供方便。這些并不立即執(zhí)行,而是等到最后一個(gè)函數(shù),統(tǒng)一執(zhí)行。 集合類關(guān)系: Collection ├List │├LinkedList │├ArrayList │└Vector │ └Stack └Set Map ...
前言 聲明,本文使用的是JDK1.8 從今天開始正式去學(xué)習(xí)Java基礎(chǔ)中最重要的東西--->集合 無(wú)論在開發(fā)中,在面試中這個(gè)知識(shí)點(diǎn)都是非常非常重要的,因此,我在此花費(fèi)的時(shí)間也是很多,得參閱挺多的資料,下面未必就做到日更了... 當(dāng)然了,如果講得有錯(cuò)的地方還請(qǐng)大家多多包涵并不吝在評(píng)論去指正~ 一、集合(Collection)介紹 1.1為什么需要Collection Java是一門面向?qū)ο蟮恼Z(yǔ)言,...
閱讀 3927·2021-11-22 09:34
閱讀 1501·2021-11-04 16:10
閱讀 1733·2021-10-11 10:59
閱讀 3281·2019-08-30 15:44
閱讀 2045·2019-08-30 13:17
閱讀 3455·2019-08-30 11:05
閱讀 752·2019-08-29 14:02
閱讀 2627·2019-08-26 13:34