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