本文基于jdk1.8來分析arraylist的源碼
首先是主要的成員變量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
/** * default initial capacity. **/ private static final int default_capacity = 10 ; /** * shared empty array instance used for empty instances. **/ private static final object[] empty_elementdata = {}; /** * shared empty array instance used for default sized empty instances. we * distinguish this from empty_elementdata to know how much to inflate when * first element is added. **/ private static final object[] defaultcapacity_empty_elementdata = {}; /** * 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 /** * the size of the arraylist (the number of elements it contains). * * @serial **/ private int size; |
其中初始大小為10,size表示集合中元素的個數(shù)。此外,還有兩個空數(shù)組empty_elementdata,和defaultcapacity_empty_elementdata。通過defaultcapacity_empty_elementdata的注釋,我們可以了解到,這個變量區(qū)別于empty_elementdata,主要是為了決定第一個元素插入時,擴容多大的問題。從這里的描述可以理解到,arraylist創(chuàng)建好后,其實并沒有真正分配數(shù)組空間,而是在第一個元素插入時,才分配的空間。這一點是區(qū)別于jdk1.6的。在jdk1.6中,arraylist一創(chuàng)建,數(shù)據(jù)空間就默認分配好了,10個或指定的空間。jdk1.8這么做,可以做到空間延遲分配,提高程序性能。
接下來看一下構(gòu)造函數(shù)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
/** * constructs an empty list with an initial capacity of ten. **/ public arraylist() { this .elementdata = defaultcapacity_empty_elementdata; } /** * constructs an empty list with the specified initial capacity. * * @param initialcapacity the initial capacity of the list * @throws illegalargumentexception if the specified initial capacity * is negative **/ public arraylist( int initialcapacity) { if (initialcapacity > 0 ) { this .elementdata = new object[initialcapacity]; } else if (initialcapacity == 0 ) { this .elementdata = empty_elementdata; } else { throw new illegalargumentexception( "illegal capacity: " + initialcapacity); } } |
無參構(gòu)造函數(shù),將創(chuàng)建一個長度為0的空數(shù)組。
有參構(gòu)造函數(shù),參數(shù)大于0時正常創(chuàng)建數(shù)組,參數(shù)為0時,也是創(chuàng)建長度為0的數(shù)組。但它和無參構(gòu)造函數(shù)創(chuàng)建的空數(shù)組是可以區(qū)別開的,它們使用了不同的對象。
接下來是插入元素add。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
/** * appends the specified element to the end of this list. * * @param e element to be appended to this list * @return <tt>true</tt> (as specified by {@link collection#add}) **/ public boolean add(e e) { ensurecapacityinternal(size + 1 ); // increments modcount!! elementdata[size++] = e; return true ; } 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; } private void ensureexplicitcapacity( int mincapacity) { modcount++; // overflow-conscious code if (mincapacity - elementdata.length > 0 ) grow(mincapacity); } |
通過calculatecapacity函數(shù),我們可以知道,如果是用new arraylist()創(chuàng)建的list,第一次add元素,計算得mincapacity = 1。如果是new arraylist(0)創(chuàng)建的list,計算得mincapacity = 10. 然后再根據(jù)mincapacity去grow。
get方法比較簡單,這里不再分析。
arraylist的一個常見問題是concurrentmodificationexception,同步修改異常,也稱為快速失敗,fast-fail。當我們以foreach方式遍歷arraylist時,如果在遍歷過程中刪除arraylist的元素,或者別的線程往arraylist中添加元素,就會拋出該異常。這里需要注意,以for(int i = 0; i < list.size(); i++)的方式遍歷arraylist時,是不會拋出同步修改異常的,但用這種方式遍歷,需要處理好i的前進速度。
那么,用foreach方式遍歷arraylist為什么會拋出同步修改異常呢?
foreach代碼的底層實現(xiàn),是用iterator對arraylist進行遍歷,在遍歷過程中,會持續(xù)調(diào)用next獲取下一個元素。next方法中,會首先checkforcomodification(),它的作用是檢查modcount和expectedmodcount是否相等。不相等時,則拋出同步修改異常。那么什么情況下修改次數(shù)和期望修改次數(shù)不相等呢?這里需要首先弄明白,modcount和expectedmodcount是什么東西?modcount是arraylist從它的父類繼承來的屬性,記錄了集合的修改次數(shù),add,remove時都會給modcount加1. expectedmodcount是迭代器的成員變量,它是在創(chuàng)建迭代器時,取的modcount的值,并且,在遍歷過程中不再改變。那么就清楚了,expectedmodcount其實是開始遍歷時modcount的值,如果在遍歷過程中,arraylist進行了add或remove操作,那么必然導(dǎo)致expectedmodcount和modcount不相等,于是就拋出了同步修改異常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public e next() { checkforcomodification(); int i = cursor; if (i >= size) throw new nosuchelementexception(); object[] elementdata = arraylist. this .elementdata; if (i >= elementdata.length) throw new concurrentmodificationexception(); cursor = i + 1 ; return (e) elementdata[lastret = i]; } final void checkforcomodification() { if (modcount != expectedmodcount) throw new concurrentmodificationexception(); } |
那么,同步修改異常如何避免呢?或者說,我們?nèi)绾伪闅v集合并把其中的某些元素刪除呢?
答案是使用迭代器的remove方法刪除元素。在迭代器的remove方法中,刪除元素后,會重新把modcount賦值給expectedmodcount,所以,它不會拋出同步修改異常。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public void remove() { if (lastret < 0 ) throw new illegalstateexception(); checkforcomodification(); try { arraylist. this .remove(lastret); cursor = lastret; lastret = - 1 ; expectedmodcount = modcount; } catch (indexoutofboundsexception ex) { throw new concurrentmodificationexception(); } } |
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對服務(wù)器之家的支持。如果你想了解更多相關(guān)內(nèi)容請查看下面相關(guān)鏈接
原文鏈接:https://blog.csdn.net/li_canhui/article/details/85001591