private?static?final?int?DEFAULT_CAPACITY?=?10;????????????????????????//?默認(rèn)容量private?static?final?Object[]?EMPTY_ELEMENTDATA?=?{};??????????????????//?空實(shí)例的空數(shù)組對象private?static?final?Object[]?DEFAULTCAPACITY_EMPTY_ELEMENTDATA?=?{};??//?也是空數(shù)組對象,用于計算添加第一個元素時要膨脹多少transient?Object[]?elementData;????????????????????????????????????????//?存儲內(nèi)容的數(shù)組private?int?size;??????????????????????????????????????????????????????//?存儲的數(shù)量
其中elementData
被聲明為了transient
,那么ArrayList是如何實(shí)現(xiàn)序列化的呢?
查看writeObject
和readObject
的源碼如下:
創(chuàng)新互聯(lián)服務(wù)項目包括故城網(wǎng)站建設(shè)、故城網(wǎng)站制作、故城網(wǎng)頁制作以及故城網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,故城網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到故城省份的部分城市,未來相信會繼續(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<size;?i++)?{ ????s.writeObject(elementData[i]); ??}?? ??if?(modCount?!=?expectedModCount)?{????throw?new?ConcurrentModificationException(); ??} }private?void?readObject(java.io.ObjectInputStream?s)?throws?java.io.IOException,?ClassNotFoundException?{ ??elementData?=?EMPTY_ELEMENTDATA;?? ??//?Read?in?size,?and?any?hidden?stuff ??s.defaultReadObject();?? ??//?Read?in?capacity ??s.readInt();?//?ignored ?? ??if?(size?>?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<size;?i++)?{ ??????a[i]?=?s.readObject(); ????} ??} }
可以看到在序列化的時候是把elementData
里面的元素逐個取出來放到ObjectOutputStream
里面的;而在反序列化的時候也是把元素逐個拿出來放回到elementData
里面的;
這樣繁瑣的操作,其中最重要的一個好處就是節(jié)省空間,因為elementData
的大小是大于ArrayList
中實(shí)際元素個數(shù)的。所以沒必要將elementData
整個序列化。
ArrayList
的構(gòu)造函數(shù)主要就是要初始化elementData
和size
,但是其中有一個還有點(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
,這個也注釋了是一個bug,那么在什么情況下會產(chǎn)生這種情況呢?
//?test?6260652private?static?void?test04()?{ ??List<String>?list?=?Arrays.asList("111",?"222",?"333"); ??System.out.println(list.getClass()); ??Object[]?objects?=?list.toArray(); ??System.out.println(objects.getClass()); } 打印:class?java.util.Arrays$ArrayListclass?[Ljava.lang.String;
這里可以看到objects
的class
居然是[Ljava.lang.String
,同時這里的ArrayList
是java.util.Arrays.ArrayList
//?java.util.Arrays.ArrayListpublic?static?<T>?List<T>?asList(T...?a)?{??return?new?ArrayList<>(a); }private?static?class?ArrayList<E>?extends?AbstractList<E>??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ù)組的具體類型,所以導(dǎo)致了以上的bug;難怪《Effective Java》里面講數(shù)組的協(xié)變設(shè)計,不是那么完美。
由于ArrayList
是基于數(shù)組的,所以他的api基本都是基于System.arraycopy()
實(shí)現(xiàn)的;
public?static?native?void?arraycopy(Object?src,?int?srcPos,?Object?dest,?int?destPos,?int?length);
可以看到這是一個native
方法,并且JVM有對這個方法做特殊的優(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ù)組是同一個數(shù)組,就可以實(shí)現(xiàn)數(shù)組內(nèi)元素的移動,
源數(shù)組和目標(biāo)數(shù)組的下標(biāo)越界,都會拋出ArrayIndexOutOfBoundsException
。
同時在ArrayList
進(jìn)行數(shù)組操作的時候都會進(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); ??} }
由于ArrayList實(shí)現(xiàn)了RandomAccess接口,它支持通過索引值去隨機(jī)訪問元素。
for?(int?i=0,?len?=?list.size();?i?<?len;?i++)?{ ????String?s?=?list.get(i); }
這其實(shí)就是迭代器模式
Iterator?iter?=?list.iterator();while?(iter.hasNext())?{ ????String?s?=?(String)iter.next(); }
這其實(shí)是一個語法糖
for?(String?s?:?list)?{ ????... }
對于ArrayList
public?void?test_List()?{ ??List<String>?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)的
對于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????//?將一個數(shù)值從操作數(shù)棧存儲到局部變量表 ????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?<?var3;?++var4)?{ ????String?s?=?var2[var4]; ????System.out.println(s); ??} }
現(xiàn)在就能很清楚的看到其實(shí)是通過隨機(jī)存儲(下標(biāo)訪問)的方式實(shí)現(xiàn)的;
fail-fast
是說當(dāng)并發(fā)的對容器內(nèi)容進(jìn)行操作時,快速的拋出ConcurrentModificationException
;但是這種快速失敗操作無法得到保證,它不能保證一定會出現(xiàn)該錯誤,但是快速失敗操作會盡最大努力拋出ConcurrentModificationException
異常。所以我們程序的正確性不能完全依賴這個異常,只應(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)存操作時,都會先使用checkForComodification
來檢測內(nèi)容是否已修改。
ArrayList
整體來看就是一個更加安全和方便的數(shù)組,但是他的插入和刪除操作也實(shí)在是蛋疼,對于這一點(diǎn)其實(shí)可以通過樹或者跳表來解決。
網(wǎng)站題目:JDK源碼分析(3)之ArrayList相關(guān)
新聞來源:http://muchs.cn/article4/ihdpie.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供自適應(yīng)網(wǎng)站、微信公眾號、企業(yè)建站、定制開發(fā)、服務(wù)器托管、營銷型網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)