java集合源碼分析(一)ArrayList

前言

我們想要更深入的去了解集合那就要通過我們?nèi)シ治鏊脑创a來了解它砸脊。希望對(duì)集合有一個(gè)更進(jìn)一步的理解而姐!

既然是看源碼那我們要怎么看一個(gè)類的源碼呢?這里我推薦的方法是:

1)看繼承結(jié)構(gòu)

看這個(gè)類的層次結(jié)構(gòu),處于一個(gè)什么位置,可以在自己心里有個(gè)大概的了解颅悉。

2)看構(gòu)造方法

在構(gòu)造方法中,看做了哪些事情粘茄,跟蹤方法中里面的方法。

3)看常用的方法

跟構(gòu)造方法一樣秕脓,這個(gè)方法實(shí)現(xiàn)功能是如何實(shí)現(xiàn)的

注:既然是源碼柒瓣,為什么要這樣設(shè)計(jì)類,有這樣的繼承關(guān)系吠架。這就要說到設(shè)計(jì)模式的問題了芙贫。所以我們要了解常用的設(shè)計(jì)模式,才能更深刻的去理解這個(gè)類傍药。

一磺平、ArrayList簡介

1.1、ArrayList概述

1)ArrayList是可以動(dòng)態(tài)增長和縮減的索引序列拐辽,它是基于數(shù)組實(shí)現(xiàn)的List類拣挪。

2)該類封裝了一個(gè)動(dòng)態(tài)再分配的Object[]數(shù)組,每一個(gè)類對(duì)象都有一個(gè)capacity屬性俱诸,表示它們所封裝的Object[]數(shù)組的長度菠劝,當(dāng)向ArrayList中添加元素時(shí),該屬性值會(huì)自動(dòng)增加睁搭。

如果想ArrayList中添加大量元素赶诊,可使用ensureCapacity方法一次性增加capacity笼平,可以減少增加重分配的次數(shù)提高性能。

3)ArrayList的用法和Vector向類似舔痪,但是Vector是一個(gè)較老的集合寓调,具有很多缺點(diǎn),不建議使用锄码。

另外夺英,ArrayList和Vector的區(qū)別是:ArrayList是線程不安全的,當(dāng)多條線程訪問同一個(gè)ArrayList集合時(shí)巍耗,程序需要手動(dòng)保證該集合的同步性秋麸,而Vector則是線程安全的。

4)ArrayList和Collection的關(guān)系:

ArrayList和Collection的關(guān)系

1.2炬太、ArrayList的數(shù)據(jù)結(jié)構(gòu)

分析一個(gè)類的時(shí)候灸蟆,數(shù)據(jù)結(jié)構(gòu)往往是它的靈魂所在,理解底層的數(shù)據(jù)結(jié)構(gòu)其實(shí)就理解了該類的實(shí)現(xiàn)思路亲族,具體的實(shí)現(xiàn)細(xì)節(jié)再具體分析炒考。

ArrayList的數(shù)據(jù)結(jié)構(gòu)是:

ArrayList的數(shù)據(jù)結(jié)構(gòu)

說明:底層的數(shù)據(jù)結(jié)構(gòu)就是數(shù)組,數(shù)組元素類型為Object類型霎迫,即可以存放所有類型數(shù)據(jù)斋枢。我們對(duì)ArrayList類的實(shí)例的所有的操作底層都是基于數(shù)組的。

二知给、ArrayList源碼分析

2.1瓤帚、繼承結(jié)構(gòu)和層次關(guān)系

繼承結(jié)構(gòu)和層次關(guān)系
繼承結(jié)構(gòu)和層次關(guān)系

我們看一下ArrayList的繼承結(jié)構(gòu):

ArrayList extends AbstractList

AbstractList extends AbstractCollection

所有類都繼承Object 所以ArrayList的繼承結(jié)構(gòu)就是上圖這樣。

分析:

1)為什么要先繼承AbstractList涩赢,而讓AbstractList先實(shí)現(xiàn)List<E>戈次?而不是讓ArrayList直接實(shí)現(xiàn)List<E>?

這里是有一個(gè)思想筒扒,接口中全都是抽象的方法怯邪,而抽象類中可以有抽象方法,還可以有具體的實(shí)現(xiàn)方法花墩,正是利用了這一點(diǎn)悬秉,讓AbstractList是實(shí)現(xiàn)接口中一些通用的方法,而具體的類冰蘑,

如ArrayList就繼承這個(gè)AbstractList類和泌,拿到一些通用的方法,然后自己在實(shí)現(xiàn)一些自己特有的方法祠肥,這樣一來允跑,讓代碼更簡潔,就繼承結(jié)構(gòu)最底層的類中通用的方法都抽取出來,

先一起實(shí)現(xiàn)了聋丝,減少重復(fù)代碼索烹。所以一般看到一個(gè)類上面還有一個(gè)抽象類,應(yīng)該就是這個(gè)作用弱睦。

2)ArrayList實(shí)現(xiàn)了哪些接口百姓?

List<E>接口:我們會(huì)出現(xiàn)這樣一個(gè)疑問,在查看了ArrayList的父類AbstractList也實(shí)現(xiàn)了List<E>接口况木,那為什么子類ArrayList還是去實(shí)現(xiàn)一遍呢垒拢?

這是想不通的地方,所以我就去查資料火惊,有的人說是為了查看代碼方便求类,使觀看者一目了然,說法不一屹耐,但每一個(gè)讓我感覺合理的尸疆,但是在stackOverFlow中找到了答案,這里其實(shí)很有趣惶岭。

網(wǎng)址貼出來 http://stackoverflow.com/questions/2165204/why-does-linkedhashsete-extend-hashsete-and-implement-sete開發(fā)這個(gè)collection 的作者Josh說寿弱。

這其實(shí)是一個(gè)mistake,因?yàn)樗麑戇@代碼的時(shí)候覺得這個(gè)會(huì)有用處按灶,但是其實(shí)并沒什么用症革,但因?yàn)闆]什么影響,就一直留到了現(xiàn)在鸯旁。

RandomAccess接口:這個(gè)是一個(gè)標(biāo)記性接口噪矛,通過查看api文檔,它的作用就是用來快速隨機(jī)存取铺罢,有關(guān)效率的問題艇挨,在實(shí)現(xiàn)了該接口的話,那么使用普通的for循環(huán)來遍歷畏铆,性能更高雷袋,例如arrayList吉殃。

而沒有實(shí)現(xiàn)該接口的話辞居,使用Iterator來迭代,這樣性能更高蛋勺,例如linkedList瓦灶。所以這個(gè)標(biāo)記性只是為了讓我們知道我們用什么樣的方式去獲取數(shù)據(jù)性能更好。

Cloneable接口:實(shí)現(xiàn)了該接口抱完,就可以使用Object.Clone()方法了贼陶。

Serializable接口:實(shí)現(xiàn)該序列化接口,表明該類可以被序列化,什么是序列化碉怔?簡單的說烘贴,就是能夠從類變成字節(jié)流傳輸,然后還能從字節(jié)流變成原來的類撮胧。

2.2桨踪、類中的屬性

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{ // 版本號(hào)
    private static final long serialVersionUID = 8683452581122892189L; // 缺省容量
    private static final int DEFAULT_CAPACITY = 10; // 空對(duì)象數(shù)組
    private static final Object[] EMPTY_ELEMENTDATA = {}; // 缺省空對(duì)象數(shù)組
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 元素?cái)?shù)組
 transient Object[] elementData; // 實(shí)際元素大小,默認(rèn)為0
    private int size; // 最大數(shù)組容量
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
}

2.3芹啥、構(gòu)造方法

ArrayList有三個(gè)構(gòu)造方法:

ArrayList構(gòu)造方法

1)無參構(gòu)造方法

/**
    * Constructs an empty list with an initial capacity of ten.  這里就說明了默認(rèn)會(huì)給10的大小锻离,所以說一開始arrayList的容量是10. */
    //ArrayList中儲(chǔ)存數(shù)據(jù)的其實(shí)就是一個(gè)數(shù)組,這個(gè)數(shù)組就是elementData墓怀,在123行定義的 private transient Object[] elementData;
   public ArrayList() {  
        super(); //調(diào)用父類中的無參構(gòu)造方法汽纠,父類中的是個(gè)空的構(gòu)造方法
        this.elementData = EMPTY_ELEMENTDATA;//EMPTY_ELEMENTDATA:是個(gè)空的Object[], 將elementData初始化傀履,elementData也是個(gè)Object[]類型虱朵。空的Object[]會(huì)給默認(rèn)大小10啤呼,等會(huì)會(huì)解釋什么時(shí)候賦值的卧秘。
    }

備注:

transient
默認(rèn)值

2)有參構(gòu)造函數(shù)一

/**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative */
    public ArrayList(int initialCapacity) {
        super(); //父類中空的構(gòu)造方法
        if (initialCapacity < 0)    //判斷如果自定義大小的容量小于0,則報(bào)下面這個(gè)非法數(shù)據(jù)異常
            throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; //將自定義的容量大小當(dāng)成初始化elementData的大小
    }

3)有參構(gòu)造方法三(不常用)

//這個(gè)構(gòu)造方法不常用官扣,舉個(gè)例子就能明白什么意思
    /* Strudent exends Person
         ArrayList<Person>翅敌、 Person這里就是泛型
        我還有一個(gè)Collection<Student>、由于這個(gè)Student繼承了Person惕蹄,那么根據(jù)這個(gè)構(gòu)造方法蚯涮,我就可以把這個(gè)Collection<Student>轉(zhuǎn)換為ArrayList<Sudent>這就是這個(gè)構(gòu)造方法的作用 */
     public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();    //轉(zhuǎn)換為數(shù)組
        size = elementData.length;   //數(shù)組中的數(shù)據(jù)個(gè)數(shù) // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class) //每個(gè)集合的toarray()的實(shí)現(xiàn)方法不一樣,所以需要判斷一下卖陵,如果不是Object[].class類型遭顶,那么久需要使用ArrayList中的方法去改造一下。
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }  

總結(jié):arrayList的構(gòu)造方法就做一件事情泪蔫,就是初始化一下儲(chǔ)存數(shù)據(jù)的容器棒旗,其實(shí)本質(zhì)上就是一個(gè)數(shù)組,在其中就叫elementData撩荣。

2.4铣揉、核心方法

2.4.1、add()方法(有四個(gè))

add()

1)boolean add(E)餐曹;//默認(rèn)直接在末尾添加元素

/**
     * Appends the specified element to the end of this list.添加一個(gè)特定的元素到list的末尾逛拱。
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add}) */
    public boolean add(E e) { //確定內(nèi)部容量是否夠了,size是數(shù)組中數(shù)據(jù)的個(gè)數(shù)台猴,因?yàn)橐砑右粋€(gè)元素朽合,所以size+1俱两,先判斷size+1的這個(gè)個(gè)數(shù)數(shù)組能否放得下,就在這個(gè)方法中去判斷是否數(shù)組.length是否夠用了曹步。
        ensureCapacityInternal(size + 1);  // Increments modCount!! //在數(shù)據(jù)中正確的位置上放上元素e宪彩,并且size++
        elementData[size++] = e; return true;
    }

分析:

ensureCapacityInternal(xxx); 確定內(nèi)部容量的方法

private void ensureCapacityInternal(int minCapacity) { if (elementData == EMPTY_ELEMENTDATA) { //看,判斷初始化的elementData是不是空的數(shù)組讲婚,也就是沒有長度 //因?yàn)槿绻强盏脑捥夯溃琺inCapacity=size+1;其實(shí)就是等于1磺樱,空的數(shù)組沒有長度就存放不了纳猫,所以就將minCapacity變成10,也就是默認(rèn)大小竹捉,但是帶這里芜辕,還沒有真正的初始化這個(gè)elementData的大小。
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        } //確認(rèn)實(shí)際的容量块差,上面只是將minCapacity=10侵续,這個(gè)方法就是真正的判斷elementData是否夠用
 ensureExplicitCapacity(minCapacity);
    }

ensureExplicitCapacity(xxx);

private void ensureExplicitCapacity(int minCapacity) {
        modCount++; // overflow-conscious code //minCapacity如果大于了實(shí)際elementData的長度憨闰,那么就說明elementData數(shù)組的長度不夠用状蜗,不夠用那么就要增加elementData的length。這里有的同學(xué)就會(huì)模糊minCapacity到底是什么呢鹉动,這里給你們分析一下

/*第一種情況:由于elementData初始化時(shí)是空的數(shù)組轧坎,那么第一次add的時(shí)候,minCapacity=size+1泽示;也就minCapacity=1缸血,在上一個(gè)方法(確定內(nèi)部容量ensureCapacityInternal)就會(huì)判斷出是空的數(shù)組,就會(huì)給
  將minCapacity=10械筛,到這一步為止捎泻,還沒有改變elementData的大小。
 第二種情況:elementData不是空的數(shù)組了埋哟,那么在add的時(shí)候笆豁,minCapacity=size+1;也就是minCapacity代表著elementData中增加之后的實(shí)際數(shù)據(jù)個(gè)數(shù)赤赊,拿著它判斷elementData的length是否夠用闯狱,如果length
不夠用,那么肯定要擴(kuò)大容量砍鸠,不然增加的這個(gè)元素就會(huì)溢出扩氢。 */

        if (minCapacity - elementData.length > 0) //arrayList能自動(dòng)擴(kuò)展大小的關(guān)鍵方法就在這里了
 grow(minCapacity);
    }

grow(xxx); arrayList核心的方法耕驰,能擴(kuò)展數(shù)組大小的真正秘密爷辱。

private void grow(int minCapacity) { // overflow-conscious code
        int oldCapacity = elementData.length;  //將擴(kuò)充前的elementData大小給oldCapacity
        int newCapacity = oldCapacity + (oldCapacity >> 1);//newCapacity就是1.5倍的oldCapacity
        if (newCapacity - minCapacity < 0)//這句話就是適應(yīng)于elementData就空數(shù)組的時(shí)候,length=0,那么oldCapacity=0饭弓,newCapacity=0双饥,所以這個(gè)判斷成立,在這里就是真正的初始化elementData的大小了弟断,就是為10.前面的工作都是準(zhǔn)備工作咏花。
            newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0)//如果newCapacity超過了最大的容量限制,就調(diào)用hugeCapacity阀趴,也就是將能給的最大值給newCapacity
            newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: //新的容量大小已經(jīng)確定好了昏翰,就copy數(shù)組,改變?nèi)萘看笮】?        elementData = Arrays.copyOf(elementData, newCapacity);
    }

hugeCapacity();

//這個(gè)就是上面用到的方法刘急,很簡單棚菊,就是用來賦最大值。
    private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow
            throw new OutOfMemoryError(); //如果minCapacity都大于MAX_ARRAY_SIZE叔汁,那么就Integer.MAX_VALUE返回统求,反之將MAX_ARRAY_SIZE返回。因?yàn)閙axCapacity是三倍的minCapacity据块,可能擴(kuò)充的太大了码邻,就用minCapacity來判斷了。 //Integer.MAX_VALUE:2147483647   MAX_ARRAY_SIZE:2147483639  也就是說最大也就能給到第一個(gè)數(shù)值另假。還是超過了這個(gè)限制像屋,就要溢出了。相當(dāng)于arraylist給了兩層防護(hù)边篮。
        return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

2)void add(int开睡,E);在特定位置添加元素苟耻,也就是插入元素

public void add(int index, E element) {
        rangeCheckForAdd(index);//檢查index也就是插入的位置是否合理篇恒。 //跟上面的分析一樣,具體看上面
        ensureCapacityInternal(size + 1);  // Increments modCount!! //這個(gè)方法就是用來在插入元素之后凶杖,要將index之后的元素都往后移一位胁艰,
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index); //在目標(biāo)位置上存放元素
        elementData[index] = element;
        size++;//size增加1
    } 

分析:

rangeCheckForAdd(index)

   //插入的位置肯定不能大于size 和小于0 //如果是,就報(bào)這個(gè)越界異常
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

System.arraycopy(...):就是將elementData在插入位置后的所有元素往后面移一位智蝠。查看api文檔

public static void arraycopy(Object src, int srcPos,
             Object dest, int destPos, int length)
src:源對(duì)象
srcPos:源對(duì)象對(duì)象的起始位置
dest:目標(biāo)對(duì)象
destPost:目標(biāo)對(duì)象的起始位置
length:從起始位置往后復(fù)制的長度腾么。 //這段的大概意思就是解釋這個(gè)方法的用法,復(fù)制src到dest杈湾,復(fù)制的位置是從src的srcPost開始解虱,到srcPost+length-1的位置結(jié)束,復(fù)制到destPost上漆撞,從destPost開始到destPost+length-1的位置上殴泰,
Copies an array from the specified source array, beginning at the specified position, to the specified position of the destination array. A subsequence of array components are copied from the source array referenced by src to the destination array referenced by dest. The number of components copied is equal to the length argument. The components at positions srcPos through srcPos+length-1
 in the source array are copied into positions destPos through destPos+length-1, respectively, of the destination array. //告訴你復(fù)制的一種情況于宙,如果A和B是一樣的,那么先將A復(fù)制到臨時(shí)數(shù)組C悍汛,然后通過C復(fù)制到B捞魁,用了一個(gè)第三方參數(shù)
If the src and dest arguments refer to the same array object, then the copying is performed as if the components at positions srcPos through srcPos+length-1 were first copied to
 a temporary array with length components and then the contents of the temporary array were copied into positions destPos through destPos+length-1 of the destination array. //這一大段,就是來說明會(huì)出現(xiàn)的一些問題离咐,NullPointerException和IndexOutOfBoundsException 還有ArrayStoreException 這三個(gè)異常出現(xiàn)的原因谱俭。
If dest is null, then a NullPointerException is thrown. 

If src is null, then a NullPointerException is thrown and the destination array is not modified. 

Otherwise, if any of the following is true, an ArrayStoreException is thrown and the destination is not modified: 

The src argument refers to an object that is not an array. 
The dest argument refers to an object that is not an array. 
The src argument and dest argument refer to arrays whose component types are different primitive types. 
The src argument refers to an array with a primitive component type and the dest argument refers to an array with a reference component type. 
The src argument refers to an array with a reference component type and the dest argument refers to an array with a primitive component type. 
Otherwise, if any of the following is true, an IndexOutOfBoundsException is thrown and the destination is not modified: 

The srcPos argument is negative. 
The destPos argument is negative. 
The length argument is negative. 
srcPos+length is greater than src.length, the length of the source array. 
destPos+length is greater than dest.length, the length of the destination array. //這里描述了一種特殊的情況,就是當(dāng)A的長度大于B的長度的時(shí)候宵蛀,會(huì)復(fù)制一部分昆著,而不是完全失敗。
Otherwise, if any actual component of the source array from position srcPos through srcPos+length-1 cannot be converted to the component type of the destination array by assignment conversion, an ArrayStoreException is thrown. 
In this case, let k be the smallest nonnegative integer less than length such that src[srcPos+k] cannot be converted to the component type of the destination array; when the exception is thrown, source array components from positions 
srcPos through srcPos+k-1 will already have been copied to destination array positions destPos through destPos+k-1 and no other positions of the destination array will have been modified. (Because of the restrictions already itemized, this paragraph effectively applies only to the situation where both arrays have component types that are reference types.) //這個(gè)參數(shù)列表的解釋术陶,一開始就說了宣吱,
Parameters:
src - the source array.
srcPos - starting position in the source array.
dest - the destination array.
destPos - starting position in the destination data.
length - the number of array elements to be copied.

總結(jié)

正常情況下會(huì)擴(kuò)容1.5倍,特殊情況下(新擴(kuò)展數(shù)組大小已經(jīng)達(dá)到了最大值)則只取最大值瞳别。

當(dāng)我們調(diào)用add方法時(shí)征候,實(shí)際上的函數(shù)調(diào)用如下:

add方法

說明:程序調(diào)用add,實(shí)際上還會(huì)進(jìn)行一系列調(diào)用祟敛,可能會(huì)調(diào)用到grow疤坝,grow可能會(huì)調(diào)用hugeCapacity。

舉例說明一:

  List<Integer> lists = new ArrayList<Integer>(6);
  lists.add(8);

說明:初始化lists大小為0馆铁,調(diào)用的ArrayList()型構(gòu)造函數(shù)跑揉,那么在調(diào)用lists.add(8)方法時(shí),會(huì)經(jīng)過怎樣的步驟呢埠巨?下圖給出了該程序執(zhí)行過程和最初與最后的elementData的大小历谍。

過程

說明:我們可以看到,在add方法之前開始elementData = {}辣垒;調(diào)用add方法時(shí)會(huì)繼續(xù)調(diào)用望侈,直至grow,最后elementData的大小變?yōu)?0勋桶,之后再返回到add函數(shù)脱衙,把8放在elementData[0]中。

舉例說明二:

  List<Integer> lists = new ArrayList<Integer>(6);
  lists.add(8);

說明:調(diào)用的ArrayList(int)型構(gòu)造函數(shù)例驹,那么elementData被初始化為大小為6的Object數(shù)組捐韩,在調(diào)用add(8)方法時(shí),具體的步驟如下:

過程

說明:我們可以知道鹃锈,在調(diào)用add方法之前荤胁,elementData的大小已經(jīng)為6,之后再進(jìn)行傳遞屎债,不會(huì)進(jìn)行擴(kuò)容處理仅政。

2.4.2垢油、刪除方法

其實(shí)這幾個(gè)刪除方法都是類似的。我們選擇幾個(gè)講已旧,其中fastRemove(int)方法是private的,是提供給remove(Object)這個(gè)方法用的召娜。

remove()
clear()
fastRemove()

1)remove(int):通過刪除指定位置上的元素

public E remove(int index) {
        rangeCheck(index);//檢查index的合理性
 modCount++;//這個(gè)作用很多运褪,比如用來檢測快速失敗的一種標(biāo)志。
        E oldValue = elementData(index);//通過索引直接找到該元素

        int numMoved = size - index - 1;//計(jì)算要移動(dòng)的位數(shù)玖瘸。
        if (numMoved > 0) //這個(gè)方法也已經(jīng)解釋過了秸讹,就是用來移動(dòng)元素的。
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved); //將--size上的位置賦值為null雅倒,讓gc(垃圾回收機(jī)制)更快的回收它璃诀。
        elementData[--size] = null; // clear to let GC do its work //返回刪除的元素。
        return oldValue;
    }

2)remove(Object):這個(gè)方法可以看出來蔑匣,arrayList是可以存放null值得劣欢。

//感覺這個(gè)不怎么要分析吧,都看得懂裁良,就是通過元素來刪除該元素凿将,就依次遍歷,如果有這個(gè)元素价脾,就將該元素的索引傳給fastRemobe(index)牧抵,使用這個(gè)方法來刪除該元素, //fastRemove(index)方法的內(nèi)部跟remove(index)的實(shí)現(xiàn)幾乎一樣侨把,這里最主要是知道arrayList可以存儲(chǔ)null值
     public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) {
                    fastRemove(index); return true;
                }
        } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) {
                    fastRemove(index); return true;
                }
        } return false;
    }

3)clear():將elementData中每個(gè)元素都賦值為null犀变,等待垃圾回收將這個(gè)給回收掉,所以叫clear

public void clear() {
        modCount++; // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

4)removeAll(collection c):

public boolean removeAll(Collection<?> c) { return batchRemove(c, false);//批量刪除
     }

5)batchRemove(xx,xx):用于兩個(gè)方法秋柄,一個(gè)removeAll():它只清楚指定集合中的元素获枝,retainAll()用來測試兩個(gè)集合是否有交集。

//這個(gè)方法骇笔,用于兩處地方映琳,如果complement為false,則用于removeAll如果為true蜘拉,則給retainAll()用萨西,retainAll()是用來檢測兩個(gè)集合是否有交集的。
   private boolean batchRemove(Collection<?> c, boolean complement) {
        final Object[] elementData = this.elementData; //將原集合旭旭,記名為A
        int r = 0, w = 0;   //r用來控制循環(huán)谎脯,w是記錄有多少個(gè)交集
        boolean modified = false; try { for (; r < size; r++) //參數(shù)中的集合C一次檢測集合A中的元素是否有,
                if (c.contains(elementData[r]) == complement) //有的話持寄,就給集合A
                    elementData[w++] = elementData[r];
        } finally { // Preserve behavioral compatibility with AbstractCollection, // even if c.contains() throws. //如果contains方法使用過程報(bào)異常
            if (r != size) { //將剩下的元素都賦值給集合A源梭,
 System.arraycopy(elementData, r, elementData, w,size - r);
                w += size - r;
            } if (w != size) { //這里有兩個(gè)用途娱俺,在removeAll()時(shí),w一直為0废麻,就直接跟clear一樣荠卷,全是為null。 //retainAll():沒有一個(gè)交集返回true烛愧,有交集但不全交也返回true油宜,而兩個(gè)集合相等的時(shí)候,返回false怜姿,所以不能根據(jù)返回值來確認(rèn)兩個(gè)集合是否有交集慎冤,而是通過原集合的大小是否發(fā)生改變來判斷,如果原集合中還有元素沧卢,則代表有交集蚁堤,而元集合沒有元素了,說明兩個(gè)集合沒有交集但狭。 // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        } return modified;
    }

總結(jié)::remove函數(shù)用戶移除指定下標(biāo)的元素披诗,此時(shí)會(huì)把指定下標(biāo)到數(shù)組末尾的元素向前移動(dòng)一個(gè)單位,并且會(huì)把數(shù)組最后一個(gè)元素設(shè)置為null立磁,

這樣是為了方便之后將整個(gè)數(shù)組不被使用時(shí)藤巢,會(huì)被GC,可以作為小的技巧使用息罗。

2.4.3掂咒、set()方法

public E set(int index, E element) { // 檢驗(yàn)索引是否合法
 rangeCheck(index); // 舊值
        E oldValue = elementData(index); // 賦新值
        elementData[index] = element; // 返回舊值
        return oldValue;
    }

說明:設(shè)定指定下標(biāo)索引的元素值

2.4.4、indexOf()方法

// 從首開始查找數(shù)組里面是否存在指定元素
    public int indexOf(Object o) { if (o == null) { // 查找的元素為空
            for (int i = 0; i < size; i++) // 遍歷數(shù)組迈喉,找到第一個(gè)為空的元素绍刮,返回下標(biāo)
                if (elementData[i]==null) return i;
        } else { // 查找的元素不為空
            for (int i = 0; i < size; i++) // 遍歷數(shù)組,找到第一個(gè)和指定元素相等的元素挨摸,返回下標(biāo)
                if (o.equals(elementData[i])) return i;
        } // 沒有找到孩革,返回空
        return -1;
    }

說明:從頭開始查找與指定元素相等的元素,注意得运,是可以查找null元素的膝蜈,意味著ArrayList中可以存放null元素的。與此函數(shù)對(duì)應(yīng)的lastIndexOf熔掺,表示從尾部開始查找饱搏。

2.4.5、get()方法

public E get(int index) { // 檢驗(yàn)索引是否合法
   rangeCheck(index); return elementData(index);
}

說明:get函數(shù)會(huì)檢查索引值是否合法(只檢查是否大于size置逻,而沒有檢查是否小于0)推沸,值得注意的是,在get函數(shù)中存在element函數(shù),element函數(shù)用于返回具體的元素鬓催,具體函數(shù)如下:

E elementData(int index) { 
    return (E) elementData[index];
 }

說明:返回的值都經(jīng)過了向下轉(zhuǎn)型(Object -> E)肺素,這些是對(duì)我們應(yīng)用程序屏蔽的小細(xì)節(jié)。

三宇驾、總結(jié)

1)arrayList可以存放null倍靡。
2)arrayList本質(zhì)上就是一個(gè)elementData數(shù)組。
3)arrayList區(qū)別于數(shù)組的地方在于能夠自動(dòng)擴(kuò)展大小课舍,其中關(guān)鍵的方法就是gorw()方法塌西。
4)arrayList中removeAll(collection c)和clear()的區(qū)別就是removeAll可以刪除批量指定的元素,而clear是全是刪除集合中的元素布卡。
5)arrayList由于本質(zhì)是數(shù)組雨让,所以它在數(shù)據(jù)的查詢方面會(huì)很快雇盖,而在插入刪除這些方面忿等,性能下降很多,有移動(dòng)很多數(shù)據(jù)才能達(dá)到應(yīng)有的效果
6)arrayList實(shí)現(xiàn)了RandomAccess崔挖,所以在遍歷它的時(shí)候推薦使用for循環(huán)贸街。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市狸相,隨后出現(xiàn)的幾起案子薛匪,更是在濱河造成了極大的恐慌,老刑警劉巖脓鹃,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逸尖,死亡現(xiàn)場離奇詭異,居然都是意外死亡瘸右,警方通過查閱死者的電腦和手機(jī)娇跟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來太颤,“玉大人苞俘,你說我怎么就攤上這事×湔拢” “怎么了吃谣?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長做裙。 經(jīng)常有香客問我岗憋,道長,這世上最難降的妖魔是什么锚贱? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任澜驮,我火速辦了婚禮,結(jié)果婚禮上惋鸥,老公的妹妹穿的比我還像新娘杂穷。我一直安慰自己悍缠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布耐量。 她就那樣靜靜地躺著飞蚓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪廊蜒。 梳的紋絲不亂的頭發(fā)上趴拧,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音山叮,去河邊找鬼著榴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛屁倔,可吹牛的內(nèi)容都是我干的脑又。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼锐借,長吁一口氣:“原來是場噩夢啊……” “哼问麸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起钞翔,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤严卖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后布轿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哮笆,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年汰扭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了稠肘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡东且,死狀恐怖启具,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情珊泳,我是刑警寧澤鲁冯,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站色查,受9級(jí)特大地震影響薯演,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜秧了,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一跨扮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦衡创、人聲如沸帝嗡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哟玷。三九已至,卻和暖如春一也,著一層夾襖步出監(jiān)牢的瞬間巢寡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來泰國打工椰苟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抑月,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓舆蝴,卻偏偏與公主長得像谦絮,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子须误,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355