private?static?final?int?DEFAULT_CAPACITY?=?10;????????????????????????//?默認(rèn)容量private?static?final?Object[]?EMPTY_ELEMENTDATA?=?{};??????????????????//?空實(shí)例的空數(shù)組對(duì)象private?static?final?Object[]?DEFAULTCAPACITY_EMPTY_ELEMENTDATA?=?{};??//?也是空數(shù)組對(duì)象,用于計(jì)算添加第一個(gè)元素時(shí)要膨脹多少transient?Object[]?elementData;????????????????????????????????????????//?存儲(chǔ)內(nèi)容的數(shù)組private?int?size;??????????????????????????????????????????????????????//?存儲(chǔ)的數(shù)量
其中elementData
被聲明為了transient
,那么ArrayList是如何實(shí)現(xiàn)序列化的呢?
查看writeObject
和readObject
的源碼如下:
創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括故城網(wǎng)站建設(shè)、故城網(wǎng)站制作、故城網(wǎng)頁制作以及故城網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,故城網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到故城省份的部分城市,未來相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
private?void?writeObject(java.io.ObjectOutputStream?s)?throws?java.io.IOException?{??//?Write?out?element?count,?and?any?hidden?stuff ??int?expectedModCount?=?modCount; ??s.defaultWriteObject();?? ??//?Write?out?size?as?capacity?for?behavioural?compatibility?with?clone() ??s.writeInt(size);?? ??//?Write?out?all?elements?in?the?proper?order. ??for?(int?i=0;?i?0)?{????//?be?like?clone(),?allocate?array?based?upon?size?not?capacity ????int?capacity?=?calculateCapacity(elementData,?size); ????SharedSecrets.getJavaOISAccess().checkArray(s,?Object[].class,?capacity); ????ensureCapacityInternal(size); ????Object[]?a?=?elementData;????//?Read?in?all?elements?in?the?proper?order. ????for?(int?i=0;?i 可以看到在序列化的時(shí)候是把
elementData
里面的元素逐個(gè)取出來放到ObjectOutputStream
里面的;而在反序列化的時(shí)候也是把元素逐個(gè)拿出來放回到elementData
里面的;
這樣繁瑣的操作,其中最重要的一個(gè)好處就是節(jié)省空間,因?yàn)?code>elementData的大小是大于ArrayList
中實(shí)際元素個(gè)數(shù)的。所以沒必要將elementData
整個(gè)序列化。二、構(gòu)造函數(shù)
ArrayList
的構(gòu)造函數(shù)主要就是要初始化elementData
和size
,但是其中有一個(gè)還有點(diǎn)意思public?ArrayList(Collection?extends?E>?c)?{ ??elementData?=?c.toArray();??if?((size?=?elementData.length)?!=?0)?{????//?c.toArray?might?(incorrectly)?not?return?Object[]?(see?6260652) ????if?(elementData.getClass()?!=?Object[].class) ??????elementData?=?Arrays.copyOf(elementData,?size,?Object[].class); ??}?else?{????//?replace?with?empty?array. ????this.elementData?=?EMPTY_ELEMENTDATA; ??} }可以看到在
Collection.toArray()
之后又判斷了他的Class
類型是不是Object[].class
,這個(gè)也注釋了是一個(gè)bug,那么在什么情況下會(huì)產(chǎn)生這種情況呢?//?test?6260652private?static?void?test04()?{ ??List?list?=?Arrays.asList("111",?"222",?"333"); ??System.out.println(list.getClass()); ??Object[]?objects?=?list.toArray(); ??System.out.println(objects.getClass()); } 打?。篶lass?java.util.Arrays$ArrayListclass?[Ljava.lang.String; 這里可以看到
objects
的class
居然是[Ljava.lang.String
,同時(shí)這里的ArrayList
是java.util.Arrays.ArrayList
//?java.util.Arrays.ArrayListpublic?static??List ?asList(T...?a)?{??return?new?ArrayList<>(a); }private?static?class?ArrayList ?extends?AbstractList ??implements?RandomAccess,?java.io.Serializable?{??private?final?E[]?a; ?? ??ArrayList(E[]?array)?{ ????a?=?Objects.requireNonNull(array); ??} ??... } 從以上例子可以看到,由于直接將外部數(shù)組的引用直接賦值給了List內(nèi)部的數(shù)組,所以List所持有的數(shù)組類型是未知的。之前講過數(shù)組是協(xié)變的,不支持泛型,所以只有值運(yùn)行時(shí)再知道數(shù)組的具體類型,所以導(dǎo)致了以上的bug;難怪《Effective Java》里面講數(shù)組的協(xié)變?cè)O(shè)計(jì),不是那么完美。
三、常用方法
由于
ArrayList
是基于數(shù)組的,所以他的api基本都是基于System.arraycopy()
實(shí)現(xiàn)的;public?static?native?void?arraycopy(Object?src,?int?srcPos,?Object?dest,?int?destPos,?int?length);可以看到這是一個(gè)
native
方法,并且JVM有對(duì)這個(gè)方法做特殊的優(yōu)化處理,private?static?void?test05()?{??int[]?s1?=?{1,?2,?3,?4,?5,?6,?7,?8,?9};??int[]?s2?=?{1,?2,?3,?4,?5,?6,?7,?8,?9}; ?? ??System.arraycopy(s1,?3,?s2,?6,?2); ??System.out.println(Arrays.toString(s1)); ??System.out.println(Arrays.toString(s2)); } 打?。?[1,?2,?3,?4,?5,?6,?7,?8,?9] [1,?2,?3,?4,?5,?6,?4,?5,?9]private?static?void?test06()?{??int[]?s1?=?{1,?2,?3,?4,?5,?6,?7,?8,?9};??int[]?s2?=?{1,?2,?3,?4,?5,?6,?7,?8,?9}; ?? ??System.arraycopy(s1,?3,?s2,?6,?5); ??System.out.println(Arrays.toString(s1)); ??System.out.println(Arrays.toString(s2)); }//?拋出異常`java.lang.ArrayIndexOutOfBoundsException`private?static?void?test07()?{??int[]?s1?=?{1,?2,?3,?4,?5,?6,?7,?8,?9};??int[]?s2?=?{1,?2,?3,?4,?5,?6,?7,?8,?9}; ?? ??System.arraycopy(s1,?8,?s2,?5,?3); ??System.out.println(Arrays.toString(s1)); ??System.out.println(Arrays.toString(s2)); }//?拋出異常`java.lang.ArrayIndexOutOfBoundsException`從上面的測試可以了解到:
System.arraycopy()
就是將源數(shù)組的元素復(fù)制到目標(biāo)數(shù)組中,如果源數(shù)組和目標(biāo)數(shù)組是同一個(gè)數(shù)組,就可以實(shí)現(xiàn)數(shù)組內(nèi)元素的移動(dòng),
源數(shù)組和目標(biāo)數(shù)組的下標(biāo)越界,都會(huì)拋出
ArrayIndexOutOfBoundsException
。同時(shí)在
ArrayList
進(jìn)行數(shù)組操作的時(shí)候都會(huì)進(jìn)行安全檢查,包括下標(biāo)檢查和容量檢查//?容量檢查public?void?ensureCapacity(int?minCapacity)?{??int?minExpand?=?(elementData?!=?DEFAULTCAPACITY_EMPTY_ELEMENTDATA)??//?any?size?if?not?default?element?table???0 ??//?larger?than?default?for?default?empty?table.?It's?already ??//?supposed?to?be?at?default?size.??:?DEFAULT_CAPACITY; ?? ??if?(minCapacity?>?minExpand)?{ ????ensureExplicitCapacity(minCapacity); ??} }四、迭代方式
1. 隨機(jī)訪問
由于ArrayList實(shí)現(xiàn)了RandomAccess接口,它支持通過索引值去隨機(jī)訪問元素。
for?(int?i=0,?len?=?list.size();?i?2. 迭代器遍歷
這其實(shí)就是迭代器模式
Iterator?iter?=?list.iterator();while?(iter.hasNext())?{ ????String?s?=?(String)iter.next(); }3. 增強(qiáng)for循環(huán)遍歷
這其實(shí)是一個(gè)語法糖
for?(String?s?:?list)?{ ????... }4. 增強(qiáng)for循環(huán)遍歷的實(shí)現(xiàn)
對(duì)于
ArrayList
public?void?test_List()?{ ??List?list?=?new?ArrayList<>(); ??list.add("a"); ??list.add("b");??for?(String?s?:?list)?{ ????System.out.println(s); ??} } 使用javap -v 反編譯
Code: ??stack=2,?locals=4,?args_size=1...????27:?invokeinterface?#7,??1????//?InterfaceMethod?java/util/List.iterator:()Ljava/util/Iterator; ????32:?astore_2????33:?aload_2????34:?invokeinterface?#8,??1????//?InterfaceMethod?java/util/Iterator.hasNext:()Z...這里可以很清楚的看到,其實(shí)是通過
Iterator
迭代器實(shí)現(xiàn)的
對(duì)于
Array
public?void?test_array()?{ ??String[]?ss?=?{"a",?"b"};??for?(String?s?:?ss)?{ ????System.out.println(s); ??} }使用javap -v 反編譯
Code: ??stack=4,?locals=6,?args_size=1 ?????0:?iconst_2?????1:?anewarray?????#2????//?class?java/lang/String ?????4:?dup?????5:?iconst_0???????????? ?????6:?ldc???????????#3????//?String?a ?????8:?aastore?????9:?dup????10:?iconst_1????11:?ldc???????????#4????//?String?b ????13:?aastore????14:?astore_1????15:?aload_1????16:?astore_2????17:?aload_2????18:?arraylength????19:?istore_3????20:?iconst_0????21:?istore????????4????//?將一個(gè)數(shù)值從操作數(shù)棧存儲(chǔ)到局部變量表 ????23:?iload?????????4????//?將局部變量加載到操作棧 ????25:?iload_3????26:?if_icmpge?????49 ????29:?aload_2????30:?iload?????????4 ????32:?aaload????33:?astore????????5 ????35:?getstatic?????#5????//?Field?java/lang/System.out:Ljava/io/PrintStream; ????38:?aload?????????5 ????40:?invokevirtual?#6????//?Method?java/io/PrintStream.println:(Ljava/lang/String;)V ????43:?iinc??????????4,?1 ????46:?goto??????????23 ????49:?return這里只能到導(dǎo)致看到是在做一些存取操作,但也不是很清楚,所以可以直接反編譯成
java
代碼public?void?test_array()?{ ??String[]?ss?=?new?String[]{"a",?"b"}; ??String[]?var2?=?ss;??int?var3?=?ss.length;?? ??for(int?var4?=?0;?var4?現(xiàn)在就能很清楚的看到其實(shí)是通過隨機(jī)存儲(chǔ)(下標(biāo)訪問)的方式實(shí)現(xiàn)的;
五、fail-fast機(jī)制
fail-fast
是說當(dāng)并發(fā)的對(duì)容器內(nèi)容進(jìn)行操作時(shí),快速的拋出ConcurrentModificationException
;但是這種快速失敗操作無法得到保證,它不能保證一定會(huì)出現(xiàn)該錯(cuò)誤,但是快速失敗操作會(huì)盡最大努力拋出ConcurrentModificationException
異常。所以我們程序的正確性不能完全依賴這個(gè)異常,只應(yīng)用于bug檢測。protected?transient?int?modCount?=?0;private?void?checkForComodification()?{??if?(ArrayList.this.modCount?!=?this.modCount)????throw?new?ConcurrentModificationException(); }在
ArrayList
的Iterator
和SubList
中,每當(dāng)進(jìn)行內(nèi)存操作時(shí),都會(huì)先使用checkForComodification
來檢測內(nèi)容是否已修改。總結(jié)
ArrayList
整體來看就是一個(gè)更加安全和方便的數(shù)組,但是他的插入和刪除操作也實(shí)在是蛋疼,對(duì)于這一點(diǎn)其實(shí)可以通過樹或者跳表來解決。
名稱欄目:JDK源碼分析(3)之ArrayList相關(guān)
本文路徑:http://weahome.cn/article/ihdpie.html