Java集合框架學(xué)習(xí)---深入探究ArrayList源碼(二)

接著Java集合框架學(xué)習(xí)---深入探究ArrayList源碼(一)繼續(xù)學(xué)習(xí)ArrayList源碼撇眯。

  • ensureCapacity函數(shù)
//當(dāng)ArrayList不處于默認(rèn)狀態(tài)時(shí),才可拓展為大小小于DEFAULT_CAPACITY容量的數(shù)組
// 否則只有指定大小超過(guò)DEFAULT_CAPACITY時(shí)才進(jìn)行擴(kuò)展旷祸;
//這個(gè)方法是public,區(qū)別于ensureCapacityInternal讼昆,這個(gè)方法是在外部使用的托享;
 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);
        }
    }

這個(gè)函數(shù)的作用主要是在當(dāng)ArrayList的容量不足以容納當(dāng)前元素時(shí)給它設(shè)置新的容量。即在有必要的時(shí)候增加ArrayList的容量以確保其能夠容納最小容量參數(shù)所指定的元素?cái)?shù)量浸赫。其實(shí)在ArrayList類(lèi)中還有幾個(gè)私有方法與ensureCapaCity方法相互調(diào)用與配合才實(shí)現(xiàn)了ensureCapaCity對(duì)外發(fā)布的功能:

//ArrayList內(nèi)部使用此方法來(lái)拓展容量
//但是假如說(shuō)處于默認(rèn)狀態(tài)闰围,拓展大小仍不能小于DEFAULT_CAPACITY
private void ensureCapacityInternal(int minCapacity) {    
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
//如果不處于默認(rèn)狀態(tài),則調(diào)用ensureExplicitCapacity進(jìn)行拓展
        ensureExplicitCapacity(minCapacity);
    }
private void ensureExplicitCapacity(int minCapacity) {
         modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/*將容量變?yōu)橹付ù笮〖认浚绻绯鼍蜁?huì)拋出OutOfMemoryError錯(cuò)誤
  *但是通過(guò)使用grow方法可以打破MAX_VALUE的限制
*/
    private void grow(int minCapacity) {
        // 用oldCapacity保存原有的容量大小
        int oldCapacity = elementData.length;
        //將新的容量設(shè)置為原來(lái)容量的3/2羡榴,可以減少由于小幅度擴(kuò)展帶來(lái)的開(kāi)銷(xiāo)
        int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果newCapacity沒(méi)有達(dá)到指定大小,則將其設(shè)定為指定大小
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
 /*如果newCapacity比MAX_ARRAY_SIZE(Integer.MAX_VALUE(2147483647)-8)
   *還大运敢,則調(diào)用hugeCapacity方法給newCapacity重新賦值
*/
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 否則就直接將該ArrayList實(shí)例拓展
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
  • contains函數(shù)
    public boolean contains(Object o)
    如果此列表中包含指定的元素校仑,則返回 true。更確切地講传惠,當(dāng)且僅當(dāng)此列表包含至少一個(gè)滿(mǎn)足 (o==null ? e==null : o.equals(e))的元素 e時(shí)迄沫,則返回 true。
public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

其實(shí)在contains內(nèi)部是通過(guò)調(diào)用了indexOf函數(shù)來(lái)實(shí)現(xiàn)功能:

public int indexOf(Object o) {
//如果傳入的對(duì)象為空卦方,則嘗試在數(shù)組中找到一個(gè)為空的元素
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
//否則就在數(shù)組中嘗試能否找到與其值相同的元素
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
//否則就返回-1
        return -1;
    }
  • indexOf函數(shù)
    public int indexOf(Object o)
    返回此列表中首次出現(xiàn)的指定元素的索引邢滑,或如果此列表不包含元素,則返回 -1(代碼見(jiàn)contains函數(shù)部分)愿汰。

  • lastIndexOf函數(shù)
    public int lastIndexOf(Object o)
    返回此列表中最后一次出現(xiàn)的指定元素的索引困后,或如果此列表不包含索引,則返回 -1衬廷。更確切地講摇予,返回滿(mǎn)足(o==null ? get(i)==null : o.equals(get(i)))
    的最高索引 i,如果不存在此類(lèi)索引吗跋,則返回 -1侧戴。

 public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
  • clone函數(shù)
    public Object clone()
    返回此ArrayList實(shí)例的淺表副本,即對(duì)此ArrayList實(shí)例進(jìn)行淺層復(fù)制
 public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }
  • toArray函數(shù)
    ArrayList中有兩個(gè)toArray函數(shù)

1.public Object[] toArray():
按適當(dāng)順序(從第一個(gè)到最后一個(gè)元素)返回包含此列表中所有元素的數(shù)組跌宛。由于該方法是分配一個(gè)新的數(shù)組酗宋,即此列表不維護(hù)對(duì)返回?cái)?shù)組的任何引用,因而它將是安全的疆拘,調(diào)用者可以自由的修改返回的數(shù)組蜕猫。需要注意的是,它通過(guò)調(diào)用Arrays中的copyOf函數(shù)來(lái)實(shí)現(xiàn)功能的哎迄。

public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }
//Arrays中的copyOf函數(shù)
@SuppressWarnings("unchecked")
/*
  *復(fù)制指定的數(shù)組回右,截取或用 null 填充(如有必要)隆圆,以使副本具有指定的長(zhǎng)度
  *對(duì)于在原數(shù)組和副本中都有效的所有索引,這兩個(gè)數(shù)組將包含相同的值翔烁。對(duì)于在副本中有效而在原數(shù)組無(wú)效的所有索引渺氧,副本將包含 null。
  *當(dāng)且僅當(dāng)指定長(zhǎng)度大于原數(shù)組的長(zhǎng)度時(shí)蹬屹,這些索引存在侣背。所得數(shù)組和原數(shù)組屬于完全相同的類(lèi)。
*/    
public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }
//被上面的 public static <T> T[] copyOf方法調(diào)用
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
//如果該數(shù)組存儲(chǔ)的是Object類(lèi)型的元素慨默,就New一個(gè)Object類(lèi)型的數(shù)組贩耐,否則就使用newInstance產(chǎn)生一個(gè)對(duì)應(yīng)類(lèi)型的數(shù)組
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
//Array中的public static Object newInstance(Class<?> componentType, int length)函數(shù)
public static Object newInstance(Class<?> componentType, int length)
        throws NegativeArraySizeException {
        return newArray(componentType, length);
    }
//

2.public <T> T[] toArray(T[] a):
按適當(dāng)順序(從第一個(gè)到最后一個(gè)元素)返回包含此列表中所有元素的數(shù)組;返回?cái)?shù)組的運(yùn)行時(shí)類(lèi)型是指定數(shù)組的運(yùn)行時(shí)類(lèi)型业筏。如果指定的數(shù)組能容納列表,則將該列表返回此處鸟赫。否則蒜胖,將分配一個(gè)具有指定數(shù)組的運(yùn)行時(shí)類(lèi)型和此列表大小的新數(shù)組。如果指定的數(shù)組能容納隊(duì)列抛蚤,并有剩余的空間(即數(shù)組的元素比隊(duì)列多)台谢,那么會(huì)將數(shù)組中緊接 collection 尾部的元素設(shè)置為 null( 在調(diào)用者知道列表中不包含任何 null 元素時(shí)才能用此方法確定列表長(zhǎng)度)。

 @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {
//size為該ArrayList的size
        if (a.length < size)
            // 必須創(chuàng)建一個(gè)與參數(shù)類(lèi)型相同的數(shù)組
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;//用來(lái)幫助調(diào)用者確定集合長(zhǎng)度(只有在明確知道集合中沒(méi)有null元素時(shí)才有用)
        return a;
    }
  • elementData方法(缺省方法)
    E elementData(int index):
    位置訪問(wèn)操作岁经,返回列表中下標(biāo)為index的元素的值
 @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }
  • get方法
    public E get(int index):
    返回此列表中指定位置上的元素(調(diào)用了上面的elementData方法).
public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

可以看到朋沮,get方法內(nèi)部也調(diào)用了一個(gè)rangeCheck,我們來(lái)看看這個(gè)方法:

 private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

這個(gè)rangeCheck方法主要是用來(lái)進(jìn)行越界判斷的:如果傳入的index比列表的size大缀壤,那么就拋出一個(gè)IndexOutOfBoundsException錯(cuò)誤樊拓。
好奇......如果index<0的話是怎么進(jìn)行數(shù)組越界判斷的呢?塘慕?我還沒(méi)找到原因筋夏,先挖個(gè)坑,等找到了方法再填坑。

  • set方法
    public E set(int index, E element):
    用指定的元素替代此列表中指定位置上的元素(返回值為被替代的元素的值)图呢。
public E set(int index, E element) {
//進(jìn)行越界判斷条篷,看看index是否大于size
        rangeCheck(index);
//取出被替代的值
        E oldValue = elementData(index);
//將指定元素替代此列表中指定位置上的元素
        elementData[index] = element;
//返回指定位置上被替代的元素
        return oldValue;
    }
  • add方法
    1.public boolean add(E e):
    將指定的元素添加到此列表的尾部(添加成功則返回true).
public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // modCount加一
        elementData[size++] = e;
        return true;
    }

其實(shí)在往列表尾部添加元素的時(shí)候要保證列表還有空間存放元素,ensureCapacityInternal函數(shù)就是用來(lái)完成此
工作的蛤织,關(guān)于ensureCapacityInternal具體的內(nèi)部實(shí)現(xiàn)參見(jiàn)上方ensureCapacity函數(shù)部分赴叹。
2.public void add(int index, E element):
將指定的元素插入此列表中的指定位置。向右移動(dòng)當(dāng)前位于該位置的元素(如果有)以及所有后續(xù)元素(將其索引加 1)指蚜。

public void add(int index, E element) {
//判斷是否越界
        rangeCheckForAdd(index);
//由于對(duì)列表結(jié)構(gòu)進(jìn)行了修改乞巧,所以必須增加modCount,關(guān)于ensureCapacityInternal參見(jiàn)上方ensureCapacity函數(shù)部分摊鸡。
        ensureCapacityInternal(size + 1); 
//將從Index的元素都往后移一個(gè)位置
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
//在指定的index位置插入需要添加的值
        elementData[index] = element;
//列表的size加一
        size++;
    }

add函數(shù)內(nèi)部還調(diào)用了rangeCheckForAdd函數(shù)摊欠,作用是判斷指定的位置是否越界丢烘。下面是rangeCheckForAdd函數(shù)源碼:

 private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
  • remove函數(shù)
    1.* public E remove(int index):*
    移除此列表中指定位置上的元素。向左移動(dòng)所有后續(xù)元素(將其索引減 1)些椒,返回值為被移出的元素播瞳。
public E remove(int index) {
//檢查下標(biāo)是否越界
        rangeCheck(index);
//由于對(duì)列表結(jié)構(gòu)進(jìn)行了修改,modCount++
        modCount++;
//保存被移出的值
        E oldValue = elementData(index);
//計(jì)算有多少元素需要向左移動(dòng)
        int numMoved = size - index - 1;
        if (numMoved > 0)
//將Index后面的元素向左移動(dòng)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
// 將第size個(gè)元素賦值為null以觸發(fā)垃圾回收免糕,并且size減一
        elementData[--size] = null; 
//返回被移出的值
        return oldValue;
    }

2.* public boolean remove(Object o):*
移除此列表中首次出現(xiàn)的指定元素(如果存在)赢乓。如果列表不包含此元素,則列表不做改動(dòng)石窑。更確切地講牌芋,移除滿(mǎn)足 (o==null ? get(i)==null : o.equals(get(i)))的最低索引的元素(如果存在此類(lèi)元素)。如果列表中包含指定的元素松逊,則返回 true(或者等同于這種情況:如果列表由于調(diào)用而發(fā)生更改躺屁,則返回 true)。

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

其實(shí)此方法內(nèi)部主要還是靠調(diào)用fastRemove來(lái)完成移出功能的经宏,以下是fastRemove源碼(其實(shí)具體實(shí)現(xiàn)和public E remove(int index)函數(shù)差不多犀暑,只不過(guò)它不會(huì)返回被移出的元素的值):

 private void fastRemove(int index) {
//由于對(duì)列表結(jié)構(gòu)進(jìn)行了修改,因此modeCount加一
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }
  • clear函數(shù)
    public void clear() :
    移除此列表中的所有元素烁兰。此調(diào)用返回后耐亏,列表將為空。
 public void clear() {
        modCount++;

        // 移出所有元素并觸發(fā)垃圾回收
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }
  • addAll函數(shù)
    1.public boolean addAll(Collection<? extends E> c):
    按照指定 collection 的迭代器所返回的元素順序沪斟,將該 collection 中的所有元素添加到此列表的尾部广辰。如果正在進(jìn)行此操作時(shí)修改指定的 collection ,那么此操作的行為是不確定的主之。(這意味著如果指定的 collection 是此列表且此列表是非空的择吊,那么此調(diào)用的行為是不確定的)。
 public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

2.public boolean addAll(int index, Collection<? extends E> c):
從指定的位置開(kāi)始槽奕,將指定 collection 中的所有元素插入到此列表中干发。向右移動(dòng)當(dāng)前位于該位置的元素(如果有)以及所有后續(xù)元素(增加其索引)。新元素將按照指定 collection 的迭代器所返回的元素順序出現(xiàn)在列表中史翘。

public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }

Java集合框架學(xué)習(xí)---深入探究ArrayList源碼(三)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末枉长,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子琼讽,更是在濱河造成了極大的恐慌必峰,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钻蹬,死亡現(xiàn)場(chǎng)離奇詭異吼蚁,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)肝匆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)粒蜈,“玉大人,你說(shuō)我怎么就攤上這事旗国】莶溃” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵能曾,是天一觀的道長(zhǎng)度硝。 經(jīng)常有香客問(wèn)我,道長(zhǎng)寿冕,這世上最難降的妖魔是什么蕊程? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮驼唱,結(jié)果婚禮上藻茂,老公的妹妹穿的比我還像新娘。我一直安慰自己玫恳,他們只是感情好辨赐,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著纽窟,像睡著了一般肖油。 火紅的嫁衣襯著肌膚如雪兼吓。 梳的紋絲不亂的頭發(fā)上臂港,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音视搏,去河邊找鬼审孽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛浑娜,可吹牛的內(nèi)容都是我干的佑力。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼筋遭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼打颤!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起漓滔,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤编饺,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后响驴,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體透且,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年豁鲤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秽誊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鲸沮。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖锅论,靈堂內(nèi)的尸體忽然破棺而出讼溺,到底是詐尸還是另有隱情,我是刑警寧澤棍厌,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布肾胯,位于F島的核電站,受9級(jí)特大地震影響耘纱,放射性物質(zhì)發(fā)生泄漏敬肚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一束析、第九天 我趴在偏房一處隱蔽的房頂上張望艳馒。 院中可真熱鬧,春花似錦员寇、人聲如沸弄慰。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)陆爽。三九已至,卻和暖如春扳缕,著一層夾襖步出監(jiān)牢的瞬間慌闭,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工躯舔, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留驴剔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓粥庄,卻偏偏與公主長(zhǎng)得像丧失,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子惜互,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容