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

關(guān)于ArrayList的動態(tài)擴(kuò)容機(jī)制解讀

 更新時(shí)間:2022年10月12日 15:52:22   作者:桐花思雨  
這篇文章主要介紹了關(guān)于ArrayList的動態(tài)擴(kuò)容機(jī)制解讀,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

1. 前言

對于 ArrayList 的動態(tài)擴(kuò)容機(jī)制想必大家都聽說過,之前的文章中也談到過,不過由于時(shí)間久遠(yuǎn),早已忘卻。

所以利用這篇文章做做筆記,加深理解記憶

2. ArrayList 的動態(tài)擴(kuò)容機(jī)制

要了解其動態(tài)擴(kuò)容機(jī)制就必須先看它的源碼,源碼是基于 jdk 1.8 的

2.1. ArrayList 的主要屬性

// 如果不指定容量(空構(gòu)造器),則在添加數(shù)據(jù)時(shí)的空構(gòu)造器默認(rèn)初始容量最小為 10
private static final int DEFAULT_CAPACITY = 10;
// 出現(xiàn)在需要用到空數(shù)組的地方,其中一處是使用自定義初始容量構(gòu)造方法時(shí)候如果你指定初始容量為0的時(shí)候,那么elementData指向該數(shù)組
// 另一處是使用包含指定collection集合元素的列表的構(gòu)造方法時(shí),如果被包含的列表中沒有數(shù)據(jù),那么elementData指向該數(shù)組
private static final Object[] EMPTY_ELEMENTDATA = {};
// 如果使用默認(rèn)構(gòu)造方法,那么elementData指向該數(shù)組
// 在添加元素時(shí)會判斷是否是使用默認(rèn)構(gòu)造器第一次添加,如果是數(shù)組就會擴(kuò)容至10個(gè)容量
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 默認(rèn)未初始化的儲存 ArrayList集合元素的底層數(shù)組,其長度就是 ArrayList的容量
transient Object[] elementData;
// 私有的elementData數(shù)組中具體的元素對象的數(shù)量,可通過size方法獲得。默認(rèn)初始值為0,在add、remove等方法時(shí)size會改變
private int size;

可以看到 ArrayList 底層核心是一個(gè) Object[] elementData 的數(shù)組

2.2. ArrayList 的構(gòu)造器

// 默認(rèn)的構(gòu)造器
public ArrayList() {
	// Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {} 空數(shù)組
	this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 自定義容量大小的構(gòu)造器
public ArrayList(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);
	}
}

從上面的構(gòu)造器中,可以得出以下結(jié)論

  • 如果使用 ArrayList 的默認(rèn)構(gòu)造器時(shí),它的初始容量就是 0
  • 如果使用 ArrayList 的有參構(gòu)造器時(shí),它的初始容量就是你傳入的參數(shù) initialCapacity的值

2.3. ArrayList 的動態(tài)擴(kuò)容

根據(jù)上面的兩個(gè)結(jié)論,不管是使用默認(rèn)或有參構(gòu)造器時(shí),我們可以使其初始容量為 0,那么它的動態(tài)擴(kuò)容發(fā)生在哪里?

可以肯定就發(fā)生在 add() 方法中,那么查看 add() 方法源碼如下

public boolean add(E e) {
	// ensureCapacityInternal() 如下
	ensureCapacityInternal(size + 1);  // Increments modCount!!
	elementData[size++] = e;
    return true;
}

按照我們第一次 add, size 肯定是 0 了,所以 ensureCapacityInternal 這個(gè)函數(shù)傳入的是 1

// 第一次 add 時(shí),參數(shù) minCapacity = 1
private void ensureCapacityInternal(int minCapacity) {
	ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// calculateCapacity() 方法
private static int calculateCapacity(Object[] elementData, int minCapacity) {	
	// 如果是第一次 add 元素
	if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
		// minCapacity 設(shè)置為 DEFAULT_CAPACITY 與 minCapacity 的最大值
		return Math.max(DEFAULT_CAPACITY, minCapacity);
	}
    return minCapacity;
}
// ensureExplicitCapacity() 方法
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
	// overflow-conscious code
	if (minCapacity - elementData.length > 0)
		grow(minCapacity);
}

elementData.length 是數(shù)組長度,它是隨著數(shù)組擴(kuò)容而動態(tài)變化的

  • 如果是第一次 add 元素時(shí),它為 0
  • 之后它是隨著 oldCapacity + (oldCapacity >> 1) 而動態(tài)變化的

首先看 calculateCapacity() 方法

  • 如果是第一次 add 元素,那么 minCapacity 設(shè)置為 DEFAULT_CAPACITY 與 minCapacity 的最大值,即 10 與 1 取最大值,這里返回最大值 10
  • 否則,直接返回 minCapacity

再看 ensureExplicitCapacity() 方法

  • 如果是第一次 add 元素,由上面方法可知 minCapacity = 10,即 10 - 0 > 0 返回 true,再調(diào)用 grow() 方法
  • 只要 minCapacity - elementData.length > 0 為 true,就會再調(diào)用 grow() 方法
private void grow(int minCapacity) {
    // 原容量
    int oldCapacity = elementData.length;
    // 擴(kuò)容后的容量,擴(kuò)大到原容量的 1.5 倍左右,右移一位相當(dāng)于原數(shù)值除以 2 的商
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 如果新容量減去最小容量的值小于 0
    if (newCapacity - minCapacity < 0)
        // 新容量等于最小容量
        newCapacity = minCapacity;
    // 如果新容量減去建議最大容量的值大于 0
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        // 調(diào)整新容量上限或者拋出 OutOfMemoryError
        newCapacity = hugeCapacity(minCapacity);
        
     // 最終進(jìn)行新數(shù)組的構(gòu)建和重新賦值,此后原數(shù)組被摒棄
     // elementData:原數(shù)組; newCapacity:新數(shù)組容量
    elementData = Arrays.copyOf(elementData, newCapacity);
}

elementData.length 是數(shù)組長度,它是隨著數(shù)組拷貝而動態(tài)變化的

  • 如果是第一次 add 元素時(shí),它為 0
  • 之后它是隨著 oldCapacity + (oldCapacity >> 1) 而動態(tài)變化的

如果是第一次 add 元素,由上面的方法可知參數(shù) minCapacity = 10,第一次 add 元素 oldCapacity = 0,可得知 newCapacity = 0 + 0,由于 newCapacity - minCapacity < 0,所以 newCapacity = minCapacity = 10 重新賦值,最終進(jìn)行新數(shù)組的構(gòu)建和拷貝賦值

3. 小結(jié)

3.1. 初始容量

如果使用 ArrayList 的默認(rèn)無參構(gòu)造器時(shí),它的初始容量就是 0

如果使用 ArrayList 的有參構(gòu)造器時(shí),它的初始容量就是你傳入的參數(shù) initialCapacity的值

3.2. 動態(tài)擴(kuò)容大小

ArrayList 的初始容量為 0,當(dāng)?shù)谝淮?add 元素時(shí),才會發(fā)生擴(kuò)容操作,它的擴(kuò)容大小是 10

當(dāng)?shù)谝淮?add 元素完成后,此時(shí)的 elementData.length = 10,后面要想發(fā)生擴(kuò)容操作,必須 minCapacity - elementData.length > 0 為 true,即 minCapacity 最小為 11,也就是說當(dāng) ArrayList 第十一次 add 時(shí),才會接著發(fā)生擴(kuò)容操作,且擴(kuò)容大小為 15 = 10 + 5

3.3. 動態(tài)擴(kuò)容大小測試

public class TestArrayList {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        list.add(7);
        list.add(8);
        list.add(9);
        list.add(10);
        list.add(11);// 打上斷點(diǎn)
    }
}

add() 方法打上斷點(diǎn)

ensureCapacityInternal() 方法打上斷點(diǎn)

ensureExplicitCapacity() 方法打上斷點(diǎn)

grow() 方法打上斷點(diǎn)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • java多線程編程之InheritableThreadLocal

    java多線程編程之InheritableThreadLocal

    這篇文章主要為大家詳細(xì)介紹了java多線程編程之InheritableThreadLocal,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • spring security自定義認(rèn)證登錄的全過程記錄

    spring security自定義認(rèn)證登錄的全過程記錄

    這篇文章主要給大家介紹了關(guān)于spring security自定義認(rèn)證登錄的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-12-12
  • Java泛型和Class類用法示例

    Java泛型和Class類用法示例

    這篇文章主要介紹了Java泛型和Class類用法,結(jié)合實(shí)例形式分析了java使用泛型限制class類避免強(qiáng)制類型轉(zhuǎn)換相關(guān)操作技巧,需要的朋友可以參考下
    2019-07-07
  • Spring?Cache簡單介紹和使用大全

    Spring?Cache簡單介紹和使用大全

    Spring?Cache是一個(gè)框架,實(shí)現(xiàn)了基于注解的緩存功能,只需要簡單地加一個(gè)注解,就能實(shí)現(xiàn)緩存功能,這篇文章主要介紹了Spring?Cache簡介和使用,需要的朋友可以參考下
    2023-03-03
  • Spring Boot中Elasticsearch的連接配置原理與使用詳解

    Spring Boot中Elasticsearch的連接配置原理與使用詳解

    在Spring Boot中,我們可以通過Elasticsearch實(shí)現(xiàn)對數(shù)據(jù)的搜索和分析,本文將介紹Spring Boot中Elasticsearch的連接配置、原理和使用方法,感興趣的可以了解一下
    2023-09-09
  • java ArrayBlockingQueue阻塞隊(duì)列的實(shí)現(xiàn)示例

    java ArrayBlockingQueue阻塞隊(duì)列的實(shí)現(xiàn)示例

    ArrayBlockingQueue是一個(gè)基于數(shù)組實(shí)現(xiàn)的阻塞隊(duì)列,本文就來介紹一下java ArrayBlockingQueue阻塞隊(duì)列的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • dubbo將異常轉(zhuǎn)換成RuntimeException的原因分析?ExceptionFilter

    dubbo將異常轉(zhuǎn)換成RuntimeException的原因分析?ExceptionFilter

    這篇文章主要介紹了dubbo將異常轉(zhuǎn)換成RuntimeException的原因分析?ExceptionFilter問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Java面試之線程通訊方式詳解

    Java面試之線程通訊方式詳解

    線程通信是指多個(gè)線程之間通過某種機(jī)制進(jìn)行協(xié)調(diào)和交互,那為什么一個(gè)線程等待和通知機(jī)制就需要這么多的實(shí)現(xiàn)方式呢?別著急,下面小編來和大家仔細(xì)聊聊
    2023-08-08
  • idea項(xiàng)目中target文件提示拒絕訪問的解決

    idea項(xiàng)目中target文件提示拒絕訪問的解決

    這篇文章主要介紹了idea項(xiàng)目中target文件提示拒絕訪問的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • Java8新特性之Optional使用詳解

    Java8新特性之Optional使用詳解

    這篇文章主要介紹了Java8新特性之Optional使用詳解,為了解決空指針異常更加優(yōu)雅,Java8?提供了?Optional?類庫,Optional?實(shí)際上是個(gè)容器,它可以保存類型T的值,或者僅僅保存null,,需要的朋友可以參考下
    2023-08-08

最新評論