欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于Java中ArrayList的源碼分析

 更新時(shí)間:2023年05月22日 09:08:34   作者:冷玩  
這篇文章主要從源碼角度帶大家深入了解一下Java中ArrayList的構(gòu)造方法和屬性等知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下

ArrayList分析

    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;

ArrayList里常用6個(gè)屬性:

四個(gè)常量屬性:

serialVersionUID:是用于在序列化和反序列化過(guò)程中進(jìn)行核驗(yàn)的一個(gè)版本號(hào)。

DEFAULT_CAPACITY:默認(rèn)的初始化容量10

EMPTY_ELEMENTDATA:空實(shí)例時(shí)的數(shù)組實(shí)例

DEFAULTCAPACITY_EMPTY_ELEMENTDATA:默認(rèn)大小的空實(shí)例時(shí)的數(shù)組實(shí)例

前兩個(gè)解釋的比較抽象

elementData:Object數(shù)組,集合真正用來(lái)存數(shù)據(jù)的容器。

size:集合大小

關(guān)于serialVersionUID可以參考文末補(bǔ)充內(nèi)容

ArrayList的三個(gè)構(gòu)造器(構(gòu)造方法)

public ArrayList() //空參
public ArrayList(int initialCapacity) //帶有初始化容量
public ArrayList(Collection<? extends E> c)//以一個(gè)集合來(lái)初始化

因?yàn)锳rrayList集合底層就是采用elementData[]數(shù)組來(lái)存儲(chǔ)數(shù)據(jù),在創(chuàng)建集合時(shí):

1、使用空參構(gòu)造器時(shí)。

只是將DEFAULTCAPACITY_EMPTY_ELEMENTDATA這個(gè)空數(shù)組賦值給了elementData數(shù)組來(lái)完成集合的初始化,因?yàn)?code>DEFAULTCAPACITY_EMPTY_ELEMENTDATA是提前創(chuàng)建好的,所以在創(chuàng)建集合時(shí)的操作只是一個(gè)賦值操作,簡(jiǎn)化了創(chuàng)建數(shù)組的時(shí)間。

transient Object[] elementData;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

2、使用有參(initialCapacity)構(gòu)造方法時(shí)。不廢話,先看源碼:

public ArrayList( @Range(from = 0, to = java.lang.Integer.MAX_VALUE) int initialCapacity){
    if (initialCapacity > 0 ) {
        this.elementData = new Object[initialCapacity];
    }else if (initialCapacity == 0) {
        this .elementData = EMPTY_ELEMENTDATA;
        }else{
            throw new IllegalArgumentException("Illegal Capacity:"+
                                               initialCapacity);
        }
}

他會(huì)首先判斷傳進(jìn)來(lái)的initialCapacity是否是大于0的:

如果不大于0他執(zhí)行的操作和使用空參構(gòu)造器執(zhí)行的操作類似,只是這次賦值給elementData數(shù)組的是EMPTY_ELEMENTDATA數(shù)組,此次空數(shù)組賦值竟然和上次空參不一樣,這是為什么呢?為什么要?jiǎng)?chuàng)建出兩個(gè)空數(shù)組呢?暫且按下不表。

如果大于0,他會(huì)新創(chuàng)建一個(gè)initialCapacity大小的數(shù)組賦值給elementData。至于傳進(jìn)來(lái)的是一個(gè)小于0的數(shù),當(dāng)然結(jié)果就是死路一條。

3、構(gòu)造方法參數(shù)是集合時(shí)。

public ArrayList(Collection<? extends E> c) {
    Object[] a = c.toArray();
    if ((size = a.length) != 0) {
        if (c.getClass() == ArrayList.class) {
            elementData = a;
        } else {
            elementData = Arrays.copyOf(a, size, Object[].class);
        }
    } else {
        // replace with empty array.
        elementData = EMPTY_ELEMENTDATA;
    }
}

他會(huì)首先將傳進(jìn)來(lái)的集合轉(zhuǎn)化為一個(gè)Object數(shù)組,然后判斷該數(shù)組是否為空。

如果為空,他仍會(huì)執(zhí)行給elementData數(shù)組賦值EMPTY_ELEMENTDATA數(shù)組的操作,到這有沒(méi)有發(fā)現(xiàn)奇妙的一點(diǎn)?無(wú)論集合第一次創(chuàng)建時(shí)采用的哪一個(gè)構(gòu)造方法,如果傳進(jìn)來(lái)的是一個(gè)“空內(nèi)容”,他都會(huì)使用底層已經(jīng)創(chuàng)建好的數(shù)組來(lái)完成初始化。

如果不為空,判斷他們兩個(gè)是否是同一個(gè)集合。如果是,將a數(shù)組直接賦值給elementData。如果不是,將a數(shù)組復(fù)制到elementData。

問(wèn)題

為什么ArrayList源碼里要定義兩個(gè)空數(shù)組呢?直接定義一個(gè)豈不是更節(jié)省空間?

可以看一下源碼中對(duì)DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的注釋:

We distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when first element is added.

大致意思就是:是為了在第一次添加元素時(shí)判斷去給數(shù)組inflate(擴(kuò)容)多少。 這就涉及到了集合在第一次添加元素時(shí)的操作和集合的擴(kuò)容。

以下時(shí)第一次添加時(shí)的源碼。

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

在第一次添加元素時(shí)會(huì)先調(diào)用ensureCapacityInternal方法,同時(shí)將添加一個(gè)元素后的集合大小size傳參過(guò)去,在ensureCapacityInternal方法中調(diào)用ensureExplicitCapacity()方法,在執(zhí)行此方法之前,會(huì)先調(diào)用calculateCapacity方法。如果此時(shí)elementDataDEFAULTCAPACITY_EMPTY_ELEMENTDATA相等,也就是采用的空參構(gòu)造器的初始化的第一次添加,則返回默認(rèn)容量和當(dāng)前容量的較大者,當(dāng)然第一次添加肯定是返回默認(rèn)容量(DEFAULT_CAPACITY)。最后進(jìn)入ensureExplicitCapacity方法,根據(jù)minCapacity - elementData.length > 0 判斷容量是否足夠,然而判斷是否要執(zhí)行擴(kuò)容方法grow,;

走到這就會(huì)發(fā)現(xiàn),ArrayList的初始化容量不會(huì)在創(chuàng)建集合時(shí)進(jìn)行,而會(huì)在第一次添加元素時(shí)進(jìn)行。而且只有采用的是空參構(gòu)造方法時(shí),才會(huì)在第一次添加元素時(shí)將最小容量設(shè)置為minCapacity = DEFAULT_CAPACITY = 10。

這還不是最終的擴(kuò)容,最終的擴(kuò)容在grow方法中:

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);//讓newCapacity等于原來(lái)容量的1.5倍
    if (newCapacity - minCapacity < 0)                 //如果newCapacity比minCapacity小
        newCapacity = minCapacity;                     //讓newCapacity等于minCapacity
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

建議

因?yàn)锳rrayList每擴(kuò)容一次都要將原數(shù)組數(shù)據(jù)復(fù)制到新數(shù)組,所以建議給定一個(gè)預(yù)估計(jì)的初始化容量,減少數(shù)組擴(kuò)容的次數(shù),這是ArrayList集合比較重要的優(yōu)化策略。

知識(shí)補(bǔ)充

serialVersionUID 是干什么的?

我們有時(shí)候在寫代碼的時(shí)候,對(duì)于一個(gè)需要序列化的類,如果不去寫 serialVersionUID,編譯器可能就會(huì)提示我們 The serializable class ClassName does not declare a static final serialVersionUID field of type long

有使用過(guò) MyBatis-plus 框架的同學(xué)應(yīng)該也發(fā)現(xiàn),在使用反向代碼生成時(shí),所生成的實(shí)體類也都帶有 static final 進(jìn)行修飾的 long 類型 serialVersionUID 。那么到底 serialVersionUID 是什么呢?有什么用?

附官方文檔連接:https://docs.oracle.com/javase/1.5.0/docs/api/java/io/Serializable.html

它是什么?

簡(jiǎn)單概括而言, serialVersionUID 是用于在序列化和反序列化過(guò)程中進(jìn)行核驗(yàn)的一個(gè)版本號(hào)。

序列化運(yùn)行時(shí)將一個(gè)版本號(hào)(稱為serialVersionUID)與每個(gè)可序列化類相關(guān)聯(lián),該版本號(hào)在反序列化期間用于驗(yàn)證序列化對(duì)象的發(fā)送方和接收方是否為該對(duì)象加載了與序列化兼容的類。

如果接收方為對(duì)象加載的類與相應(yīng)發(fā)送方類的serialVersionId不同,則反序列化將導(dǎo)致InvalidClassException。

可序列化類可以通過(guò)聲明名為 serialVersionUID 的字段顯式聲明自己的 serialVersionUID,且該字段必須是static、final的且類型為long

ANY-ACCESS-MODIFIER static final long serialVersionUID=42L;

不聲明會(huì)怎樣?

如Java(TM)對(duì)象序列化規(guī)范中所講述的,如果可序列化類沒(méi)有顯式聲明serialVersionUID,則序列化運(yùn)行時(shí)將根據(jù)類的各個(gè)方面計(jì)算該類的默認(rèn)serialVersionUID值。

但是,強(qiáng)烈建議所有可序列化類顯式聲明serialVersionUID值,因?yàn)槟J(rèn)的 serialVersionUID 計(jì)算對(duì)類詳細(xì)信息高度敏感,這些詳細(xì)信息可能因編譯器實(shí)現(xiàn)而異,因此在反序列化過(guò)程中可能會(huì)導(dǎo)致意外的InvalidClassExceptions。

因此,為了保證在不同的java編譯器實(shí)現(xiàn)中SerialVersionId值是一致的,可序列化類必須聲明一個(gè)顯式的SerialVersionId值。還強(qiáng)烈建議顯式 serialVersionUID 聲明盡可能使用 private 修飾符,因?yàn)榇祟惵暶鲀H適用于立即聲明的類——serialVersionUID字段不可用作繼承成員。

其他問(wèn)題

Q: 如果父類被序列化,默認(rèn)情況下子類也被序列化,所以我們也需要為 child 聲明 serialVersionUID 嗎?

A: 建議對(duì)子類,或者說(shuō)每一個(gè)存在序列化需求的類都進(jìn)行 serialVersionUID 的指定,并且如上建議,采用 private 進(jìn)行修飾,避免子類對(duì)父類的 protected 繼承(我還沒(méi)碰上炸毛的情況,所以也不好講繼承后會(huì)在什么情況下出現(xiàn)什么樣的問(wèn)題)

Q: 如果我不序列化,還需要指定嗎?

A:如果不存在序列化需求,也就不存在序列化與反序列化中的比對(duì),原則上不聲明 serialVersionUID 也是可以的

到此這篇關(guān)于關(guān)于Java中ArrayList的源碼分析的文章就介紹到這了,更多相關(guān)Java ArrayList內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論