小編給大家分享一下java中ArrayList怎么用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
成都創(chuàng)新互聯(lián)公司-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比淄博網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式淄博網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋淄博地區(qū)。費(fèi)用合理售后完善,十年實(shí)體公司更值得信賴。
ArrayList構(gòu)造器簡介
在java中,一切皆對(duì)象,一切操作又離不開對(duì)象,ArrayList也是如此,所以想要研究ArrayList是如何工作的就要從ArrayList的構(gòu)造開始。
ArrayList提供了三個(gè)構(gòu)造,分別是不帶任何參數(shù)的構(gòu)造器、接受一個(gè)int類型值的構(gòu)造器和接受一個(gè)帶泛型的Collection的構(gòu)造器,首先我們從最簡單的沒有任何參數(shù)、也是大多數(shù)人最常用的這個(gè)構(gòu)造器開始。
無參數(shù)構(gòu)造器
打開源碼可以看到無參數(shù)構(gòu)造器非常的簡單,只有一行代碼,代碼如下:
其中DEFAULTCAPACITY_EMPTY_ELEMENTDATA是ArrayList中定義的一個(gè)靜態(tài)的不可變的空數(shù)組,而elementData則是ArrayList保存實(shí)際數(shù)據(jù)使用的數(shù)組引用,也就是說如果用默認(rèn)無參數(shù)構(gòu)造器構(gòu)造ArrayList的話是沒有初始數(shù)組的(為空,所有實(shí)例共享)。
接收一個(gè)int類型值的構(gòu)造器
打開源碼,可以看到如下代碼:
可以看到,當(dāng)傳入?yún)?shù)為0時(shí)與不傳參數(shù)的邏輯是一致的,當(dāng)傳入?yún)?shù)大于0時(shí)ArrayList會(huì)構(gòu)建一個(gè)與傳入?yún)?shù)為大小的Object數(shù)組,而當(dāng)傳入?yún)?shù)小于0時(shí),就會(huì)拋出一個(gè)非法參數(shù)異常(IllegalArgumentException)了。
接收一個(gè)Collection參數(shù)的構(gòu)造器
仍然是打開源碼查看,可以看到以下代碼:
首先是將傳入?yún)?shù)轉(zhuǎn)換為數(shù)組,然后將這個(gè)數(shù)組引用賦值給ArrayList內(nèi)部維護(hù)的數(shù)組引用,如果這個(gè)數(shù)組的長度是0時(shí)你會(huì)發(fā)現(xiàn)這時(shí)的行為又與空參數(shù)構(gòu)造器的行為一致了,而當(dāng)這個(gè)數(shù)組長度不是0時(shí),ArrayList做了一些小動(dòng)作,對(duì)于這些小動(dòng)作,注釋也寫的很清楚了,c.toArray might (incorrectly) not return Object[],也就是說這個(gè)數(shù)組有可能不是Object類型的數(shù)組(在不正確的情況下,這個(gè)是由JDK本身的一個(gè)BUG造成的,具體的可以去JDK的BUG庫http://bugs.java.com/bugdatabase/中查找BUG ID為6260652的BUG,有興趣的同學(xué)可以自行查找閱讀,本文不去詳細(xì)介紹),所以為了保證代碼的健壯性,就加了這么一個(gè)類型檢查,而為什么要檢查這個(gè)數(shù)組是不是Object類型的呢?這個(gè)很簡單,就是因?yàn)槿绻颂幰玫氖且粋€(gè)String類型的數(shù)組,后續(xù)如果往里邊放入一個(gè)非String類型的數(shù)據(jù)就會(huì)報(bào)錯(cuò),而這個(gè)錯(cuò)誤通常是到運(yùn)行時(shí)才會(huì)發(fā)現(xiàn)的。
ArrayList的構(gòu)造器介紹到此就結(jié)束了,下面開始介紹ArrayList的方法,由于篇幅有限,所以本文僅挑選幾個(gè)ArrayList初級(jí)使用者最常用的方法介紹。
public boolean add(E e)
這個(gè)方法是最常用的一個(gè)方法,其作用就是將一個(gè)新元素加入到當(dāng)前l(fā)ist的尾部,源碼如下:
可以看到這個(gè)方法非常簡潔,調(diào)用了另外一個(gè)方法ensureCapacityInternal,然后將數(shù)據(jù)填充進(jìn)數(shù)組,更新size,整個(gè)方法就結(jié)束了,固定返回的true,也就是該方法只要不拋出異??隙〞?huì)返回true,至于ensureCapacityInternal方法會(huì)在后面講解,ArrayList中也多次調(diào)用到了該方法,算是核心方法之一??梢院苋菀卓闯觯摲椒ǖ臐u進(jìn)時(shí)間復(fù)雜度為O(1)(不擴(kuò)容的情況下)。
public void add(int index, E element)
這個(gè)方法的作用是將一個(gè)元素插入到list的指定位置,源碼如下:
首先是第一行,第一行很簡單,看方法名就知道是范圍檢測(cè),具體的行為就是檢測(cè)當(dāng)前傳入的index參數(shù)是否大于當(dāng)前size或者小于0,如果大于當(dāng)前size或者小于0的話就拋出一個(gè)索引越界異常(IndexOutOfBoundsException),然后該方法也調(diào)用了ensureCapacityInternal方法,然后就是將參數(shù)index位置和index位置之后的數(shù)據(jù)給全部后移一位,也就是說該方法插入的位置后邊的數(shù)據(jù)越多,那么要移位的數(shù)據(jù)也就越多,效率也就越低。該方法的最壞時(shí)間復(fù)雜度為O(n)(不擴(kuò)容的情況下),其中n等于ArrayList的size。
public E remove(int index)
該方法是將指定索引處的數(shù)據(jù)刪除,源碼如下:
該方法的第一行仍然是范圍檢查,不同的是只要index不是大于等于ArrayList的size就不會(huì)拋出異常,但是這一行不拋出異常不代表后邊就不會(huì)拋出異常,該處沒有顯示檢查是否小于0是因?yàn)楹筮叺谌杏袕臄?shù)組中取數(shù)據(jù)這個(gè)操作,而對(duì)于該操作,如果index小于0的話是一定會(huì)拋出異常的。然后第四行計(jì)算了一下需要移動(dòng)位置的元素,也就是index位置之后還有多少元素需要前移,當(dāng)該值大于0時(shí)(注:只會(huì)大于0或者等于0,不可能小于0),說明有需要移動(dòng)的元素,將這numMoved個(gè)元素統(tǒng)統(tǒng)前移一位,然后將原數(shù)組的最后一位設(shè)置為null以便于GC可以清除該處引用(注:因?yàn)槭褂玫腟ystem.arraycopy方法,所以并不會(huì)刪除數(shù)據(jù),只會(huì)復(fù)制,而原來數(shù)組的最后一位雖然已經(jīng)復(fù)制前移,而且size也已經(jīng)更新,獲取不到該引用的數(shù)據(jù),但是該引用仍然存在,并沒有被清空,所以下次GC的時(shí)候如果該處沒有填充新的值覆蓋,那么這個(gè)引用將不會(huì)被回收并且也獲取不到該處的值,需要詳細(xì)了解的可以看一下java的GC原理或者加我Q1213812243詢問^_^)。最后,將第三行獲取到的該位置的數(shù)據(jù)返回,至此,該方法結(jié)束。由于該方法也需要原數(shù)組數(shù)據(jù)的移位,所以很容易可以得出該方法的最壞時(shí)間復(fù)雜度也是O(n),其中n等于ArrayList的size。
public boolean remove(Object o)
該方法是從ArrayList中刪除指定元素,更準(zhǔn)確的說是從ArrayList中刪除第一個(gè)與指定元素相同的元素,話不多說上源碼:
由該方法的實(shí)現(xiàn)可以看出,如果傳入的元素為null時(shí),該方法將刪除ArrayList中的第一個(gè)null元素(不是全部的null元素)并返回true,如果傳入元素不為null時(shí),該方法將調(diào)用傳入元素的equals方法與ArrayList中的元素一一對(duì)比,直到發(fā)現(xiàn)第一個(gè)相同的元素,然后刪除并返回true,如果遍歷整個(gè)list發(fā)現(xiàn)都沒有與傳入對(duì)象相同的對(duì)象,那么該方法將返回false表示刪除失敗。這里要注意一個(gè)小細(xì)節(jié),該方法實(shí)際刪除元素操作用的是fastRemove而不是上一個(gè)提到的public E remove(int index),而根據(jù)命名可以猜測(cè)出fastRemove方法刪除是比較快的,但是具體是哪兒快了呢?首先查看源碼:
與上一個(gè)remove方法對(duì)比,很容易可以看出這個(gè)方法由于是內(nèi)部使用的,也不需要返回值,所以并沒有去檢查參數(shù)index的范圍合法性,也沒有把要?jiǎng)h除的數(shù)據(jù)取出,省了這兩個(gè)開銷,雖然省的并不多,但是如果在一個(gè)大型系統(tǒng)中,這樣的小開銷累計(jì)起來也是很大的。同樣的,很容易可以得出該方法的時(shí)間漸進(jìn)復(fù)雜度為O(n),其中n等于ArrayList的size。
public E get(int index)
該方法是從ArrayList中取數(shù)據(jù),數(shù)據(jù)存起來的目的就是為了后續(xù)的使用,所以該方法是最常用也是最重要的一個(gè)方法,源碼如下:
可以看出,該方法很簡單,僅僅檢查了一下參數(shù)的范圍,然后就直接取數(shù)據(jù)返回了,而elementData方法也只有短短的一行
很容易可以得出該方法的時(shí)間漸進(jìn)復(fù)雜度為O(1)。
private void ensureCapacityInternal(int minCapacity)
這個(gè)方法是前面提及的但是并沒有具體介紹的一個(gè)方法,把這個(gè)方法放在最后是因?yàn)檫@個(gè)方法算是ArrayList的一個(gè)核心方法,該方法的作用就是檢查內(nèi)部數(shù)組是否夠用,如果不夠用的話自動(dòng)擴(kuò)充,源碼如下:
該方法又調(diào)用了ensureExplicitCapacity方法,而ensureExplicitCapacity方法的源碼如下:
該方法判斷了一下參數(shù)是否大于當(dāng)前數(shù)組的最大長度,如果大于那么調(diào)用grow方法擴(kuò)容,如果不大于那么不做任何操作。grow方法源碼如下:
該方法也很簡單,只有7行代碼,在第一行首先計(jì)算出原數(shù)組的長度,然后第二行計(jì)算出原數(shù)組長度擴(kuò)充1.5倍后的新的長度(注:右移一位等價(jià)于除以2),第三行判斷了擴(kuò)充1.5倍后的長度是否大于參數(shù)(實(shí)際需要的長度),如果小于該長度的話將擴(kuò)充后的新長度更新為參數(shù)值(minCapacity),然后在第5行判斷了擴(kuò)充后的長度是否大于MAX_ARRAY_SIZE這個(gè)靜態(tài)變量,如果大于該長度的話使用hugeCapacity方法確定是否溢出(即數(shù)組長度是否大于Integer.MAX_VALUE),該方法進(jìn)入后正常情況是肯定會(huì)返回Integer.MAX_VALUE,因?yàn)橥獠恳呀?jīng)判斷過minCapacity是否大于MAX_ARRAY_SIZE了,只有大于該值才能進(jìn)來,所以此方法必定返回Integer.MAX_VALUE,ArrayList內(nèi)部維護(hù)的數(shù)組長度也達(dá)到了最大值Integer.MAX_VALUE,而此時(shí)當(dāng)list再次填充滿需要擴(kuò)容的時(shí)候,傳到此處的參數(shù)為(size+1)將是一個(gè)負(fù)數(shù)(Integer溢出),然后調(diào)用hugeCapacity方法時(shí)會(huì)發(fā)現(xiàn)傳進(jìn)來的參數(shù)是一個(gè)負(fù)數(shù),說明已經(jīng)溢出了,將會(huì)拋出一個(gè)異常。(注:如果對(duì)此處的溢出不理解可以加QQ1213812243詢問或者自行百度)
該方法的最后一行會(huì)將數(shù)據(jù)從老數(shù)組遷移至新數(shù)組,而老數(shù)組由于沒有了引用所以會(huì)在后續(xù)的GC中被清除。很容易可以得出該方法的時(shí)間漸進(jìn)復(fù)雜度為O(n)(需要擴(kuò)容的情況下,不需要擴(kuò)容為O(1)),其中n等于當(dāng)前ArrayList的size。
以上是“java中ArrayList怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!