JDK源碼分析(3)之ArrayList相關(guān)-創(chuàng)新互聯(lián)

一、成員變量

private?static?final?int?DEFAULT_CAPACITY?=?10;????????????????????????//?默認(rèn)容量private?static?final?Object[]?EMPTY_ELEMENTDATA?=?{};??????????????????//?空實例的空數(shù)組對象private?static?final?Object[]?DEFAULTCAPACITY_EMPTY_ELEMENTDATA?=?{};??//?也是空數(shù)組對象,用于計算添加第一個元素時要膨脹多少transient?Object[]?elementData;????????????????????????????????????????//?存儲內(nèi)容的數(shù)組private?int?size;??????????????????????????????????????????????????????//?存儲的數(shù)量

其中elementData被聲明為了transient,那么ArrayList是如何實現(xiàn)序列化的呢?
查看writeObjectreadObject的源碼如下:

鐘祥網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián),鐘祥網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為鐘祥千余家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)營銷網(wǎng)站建設(shè)要多少錢,請找那個售后服務(wù)好的鐘祥做網(wǎng)站的公司定做!
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ù)的。所以沒必要將elementData整個序列化。

二、構(gòu)造函數(shù)

ArrayList的構(gòu)造函數(shù)主要就是要初始化elementDatasize,但是其中有一個還有點意思

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());
}

打?。篶lass?java.util.Arrays$ArrayListclass?[Ljava.lang.String;

這里可以看到objectsclass居然是[Ljava.lang.String,同時這里的ArrayListjava.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é)變的,不支持泛型,所以只有值運行時再知道數(shù)組的具體類型,所以導(dǎo)致了以上的bug;難怪《Effective Java》里面講數(shù)組的協(xié)變設(shè)計,不是那么完美。

三、常用方法

由于ArrayList是基于數(shù)組的,所以他的api基本都是基于System.arraycopy()實現(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ù)組,就可以實現(xiàn)數(shù)組內(nèi)元素的移動,

  • 源數(shù)組和目標(biāo)數(shù)組的下標(biāo)越界,都會拋出ArrayIndexOutOfBoundsException。

同時在ArrayList進行數(shù)組操作的時候都會進行安全檢查,包括下標(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. 隨機訪問

由于ArrayList實現(xiàn)了RandomAccess接口,它支持通過索引值去隨機訪問元素。

for?(int?i=0,?len?=?list.size();?i?<?len;?i++)?{
????String?s?=?list.get(i);
}

2. 迭代器遍歷

這其實就是迭代器模式

Iterator?iter?=?list.iterator();while?(iter.hasNext())?{
????String?s?=?(String)iter.next();
}

3. 增強for循環(huán)遍歷

這其實是一個語法糖

for?(String?s?:?list)?{
????...
}

4. 增強for循環(huán)遍歷的實現(xiàn)

  • 對于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...

這里可以很清楚的看到,其實是通過Iterator迭代器實現(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)在就能很清楚的看到其實是通過隨機存儲(下標(biāo)訪問)的方式實現(xiàn)的;

五、fail-fast機制

fail-fast是說當(dāng)并發(fā)的對容器內(nèi)容進行操作時,快速的拋出ConcurrentModificationException;但是這種快速失敗操作無法得到保證,它不能保證一定會出現(xiàn)該錯誤,但是快速失敗操作會盡大努力拋出ConcurrentModificationException異常。所以我們程序的正確性不能完全依賴這個異常,只應(yīng)用于bug檢測。

protected?transient?int?modCount?=?0;private?void?checkForComodification()?{??if?(ArrayList.this.modCount?!=?this.modCount)????throw?new?ConcurrentModificationException();
}

ArrayListIteratorSubList中,每當(dāng)進行內(nèi)存操作時,都會先使用checkForComodification來檢測內(nèi)容是否已修改。

總結(jié)

ArrayList整體來看就是一個更加安全和方便的數(shù)組,但是他的插入和刪除操作也實在是蛋疼,對于這一點其實可以通過樹或者跳表來解決。

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。

當(dāng)前題目:JDK源碼分析(3)之ArrayList相關(guān)-創(chuàng)新互聯(lián)
網(wǎng)頁路徑:http://bm7419.com/article0/dssgoo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站制作、搜索引擎優(yōu)化手機網(wǎng)站建設(shè)、品牌網(wǎng)站制作定制網(wǎng)站、企業(yè)網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)