Java集合中的ArrayList

? ? ? ? 今天我們來聊聊Java集合中的ArrayList,說起集合在我們編碼過程中使用頻率最高的了。這一點(diǎn)大家沒有異議吧,接下來我們由淺入深逐步認(rèn)識一下ArrayList。

1.Java集合的設(shè)計(jì)


設(shè)計(jì)層次
繼承接口

? ? ? ? 上圖是Java集合的設(shè)計(jì)層次和java類繼承接口列表,Java設(shè)計(jì)策略先由接口定義集合應(yīng)該具有的功能,接口設(shè)計(jì)完成后在具體類中實(shí)現(xiàn)具有這些功能的類。另外java集合提供了Collections工具類,它已經(jīng)實(shí)現(xiàn)了對集合排序,遍歷等算法,我們可以使用它,快速的完成集合的排序、遍歷等功能。同樣我們今天要聊到的ArrayList他繼承List,擁有Collection接口定義的功能,同樣可以使用Collections工具類對集合進(jìn)行排序、遍歷等。

2.ArrayList源碼分析

? ? ? ? 關(guān)于ArrayList的原理,通過查看package java.util.ArrayList實(shí)現(xiàn),下面截圖部分是ArrayList的兩個構(gòu)造方法,一個是無參數(shù)一個是帶有長度參數(shù)構(gòu)造方法的代碼,閱讀代碼我們可以發(fā)現(xiàn)ArrayList的底層是基于一個Object數(shù)組實(shí)現(xiàn)的。


ArrayList無參構(gòu)造函數(shù)
ArrayList帶有長度的參構(gòu)造函數(shù)

? ? ? ? 既然知道了ArrayList底層是數(shù)組,故ArrayList擁有數(shù)組同樣特性,比如可以通過索引獲取指定位置的數(shù)據(jù),也可以使用indexOf找到某個對象的索引位置。既然是數(shù)組在初始化創(chuàng)建對象的時候必須指定長度。


ArrayList默認(rèn)長度


3.ArrayList擴(kuò)容原理

? ? ? ? 源碼中指定ArrayList的默認(rèn)初始大小為10的數(shù)組,長度為10的Object數(shù)組很容易就裝滿了,如果增加的元素個數(shù)超過了10個,那么ArrayList底層會新生成一個數(shù)組,長度為原來數(shù)組的1.5倍+1,然后將原數(shù)組的內(nèi)容復(fù)制到新數(shù)組中,并且后續(xù)增加的內(nèi)容都會放到新數(shù)組當(dāng)中,當(dāng)新數(shù)組無法容納增加的元素時,重復(fù)該過程。下圖可以看到ArrayList擴(kuò)容過程


ArrayList擴(kuò)容過程

? ? ? ? JVM操作自動擴(kuò)容的時候非常消耗性能,所以在選取使用ArrayList的時候最好事先估算一下它的長度,然后創(chuàng)建對象的時候傳入估算的值。盡量避免ArrayList自動擴(kuò)容,也有人會這樣考慮,初始化ArrayList指定一個足夠大的數(shù)據(jù),雖然避免的自動擴(kuò)容,但是造成內(nèi)存的浪費(fèi)。ArrayList既然是事先分配的數(shù)組,分配的數(shù)組每個項(xiàng)的長度是固定的,那么如何使用ArrayList存放復(fù)雜的對象呢?

4.ArrayList數(shù)據(jù)存儲原理

? ? ? ? ArrayList是一個Object數(shù)組,如果ArrayList存放基本類型,可以直接存放到數(shù)組中,但使用ArrayList存放對象,事先分配的Object無法存放形式和大小各異的對象,那么ArrayList進(jìn)行了優(yōu)化,在ArrayList集合中存放的是對象的引用,而不是對象本身。


ArrayList存儲對象

? ? ? ? ArrayList巧妙的使用存對象引用的方法解決了復(fù)雜對象的存取問題,這樣的設(shè)計(jì)類似C++的指針,存取的索引地址可以方便方位索引位置的對象信息。

5.ArrayList插入和查詢數(shù)據(jù)原理

? ? ? ? 接下來我們透過ArrayList的具體方法操作,看ArrayList是如何進(jìn)行元素的收集的,因?yàn)锳rrayList底層本質(zhì)是數(shù)組,且實(shí)現(xiàn)了List接口,使用中可以方便調(diào)用List接口定義的add()功能向ArrayList添加內(nèi)容。


ArrayList添加內(nèi)容

? ? ? ? 可以發(fā)現(xiàn),向ArrayList中插入數(shù)據(jù),如果插入的數(shù)據(jù)位于數(shù)組末尾性能非???,但是向數(shù)組中間插入就需要將插入位置后面的所有數(shù)據(jù)順序后移,非常消耗性能,同理remove 集合ArrayList中的一個數(shù)據(jù),也會導(dǎo)致冗長的移位操作,性能消耗得不償失。所以對于ArrayList最好不要輕易改變帶索引的數(shù)據(jù)。

? ? ? ? 知道了數(shù)據(jù)的增刪之后,再來看看ArrayList進(jìn)行數(shù)據(jù)訪問是如何實(shí)現(xiàn)的?下圖是ArrayList定義的獲取值的方法


ArrayList通過索引獲取對象

? ? ? ? 代碼中ArrayList需要傳入索引地址,方法定義通過索引隨機(jī)訪問數(shù)組索引位置的數(shù)據(jù)。它的訪問時間復(fù)雜度O(1),性能非常的快。所以ArrayList適合一次加載多次訪問的數(shù)據(jù)非常的適合。既然ArrayList在查詢方面性能如此適合,那么可以直接使用ArrayList做多線程公共數(shù)據(jù)的查詢服務(wù)嗎?答案是不可以的。

6.ArrayList安全性淺析和應(yīng)對方法

? ? ? ? 因?yàn)锳rrayList是線程不安全的,ArrayList的操作并非是原子性的,通讀ArrayList代碼并沒有實(shí)現(xiàn)線程同步機(jī)制的加鎖約束。ArrayList添加一個元素的時候,需要兩個步驟,第一步在Items[Size]的位置存放此元素,第二步增大Size的值,在單線程運(yùn)行的情況,這兩個步驟是順序執(zhí)行的,互相不會影響。但是如果有兩個線程去操作呢?

? ? ? ? 線程A在0的位置賦了一個值,然后停下來,B線程ArrayList 0的位置又賦了一個值,其實(shí)是重復(fù)在一個位置賦值,然后回到A線程,執(zhí)行Size增加,也就是ArrayList的大小增加了,原來Size是1,現(xiàn)在變成2,然后停下來繼續(xù)執(zhí)行線程B,又增加了一個空間位置,size大小就變成了3,結(jié)果就是0的位置有值,1和2的索引位置都沒有值實(shí)際大小是3,跟想要的結(jié)果0和1賦不同的值,結(jié)果不對。那么如何來解決這個問題呢?

? ? ? ? 在JVM的ArrayList設(shè)計(jì)的時候給出了兩個方法可以讓程序員既能利用ArrayList的隨機(jī)訪問的高效性能,又能避免并發(fā)訪問線程安全問題。

方法一、繼承Arraylist,然后重寫或按需求編寫自己的方法,這些方法要寫成synchronized,在這些synchronized的方法中調(diào)用ArrayList的方法。

方法二、使用Collections.synchronizedList的接口,如下使用:

List list =Collections.synchronizedList(new ArrayList());

方法一可以實(shí)現(xiàn),但是對于程序開發(fā)者來說加大了工作難度,列在這里供參考。為了使用方便推薦直接使用方法二,因?yàn)镃ollections.synchronizedList已經(jīng)實(shí)現(xiàn)了ArrayList的線程安全,所以不用重復(fù)造輪子了。

? ? ? ? 通過今天的分享希望大家對ArrayList有深入的了解。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在一個方法內(nèi)部定義的變量都存儲在棧中,當(dāng)這個函數(shù)運(yùn)行結(jié)束后,其對應(yīng)的棧就會被回收,此時,在其方法體中定義的變量將不...
    Y了個J閱讀 4,547評論 1 14
  • Java繼承關(guān)系初始化順序 父類的靜態(tài)變量-->父類的靜態(tài)代碼塊-->子類的靜態(tài)變量-->子類的靜態(tài)代碼快-->父...
    第六象限閱讀 2,246評論 0 9
  • 誤會 早年在美國阿拉斯加地方,有一對年輕人結(jié)婚,婚后生育,他的太太因難產(chǎn)而死,遺下一孩子。 他忙生活,又忙于看家,...
    威樂河灣閱讀 500評論 0 1
  • 今天我要說的是,這么多年我把自己排除在外,以為不走進(jìn),就不會傷害!但是發(fā)現(xiàn),現(xiàn)實(shí)的生活也好,人也罷,并不會因?yàn)槟愕?..
    翻滾吧逆流而上的魚閱讀 299評論 0 0

友情鏈接更多精彩內(nèi)容