Java集合 - ArrayList底層源碼解析

下面開始對 Java 中 ArrayList 的深度源碼分析,基于 JDK 8 的實現(后續版本略有差異,但核心邏輯一致)。我們將從 類結構、擴容機制、核心方法實現、性能優化、線程安全問題 等角度進行詳細解析

一、類結構與核心字段

1. 類繼承關系

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
  • AbstractList:提供了 List 接口的部分默認實現(如 get(int index)size() 等)
  • RandomAccess:標記接口,表示支持隨機訪問(通過索引訪問元素,O(1) 時間復雜度)
  • Cloneable:支持克隆
  • Serializable:支持序列化

2. 核心字段

// 默認初始容量
private static final int DEFAULT_CAPACITY = 10;
// 空數組常量(用于無參構造)
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 空數組常量(用于指定容量為 0 的構造)
private static final Object[] EMPTY_ELEMENTDATA = {};
// 存儲元素的數組
transient Object[] elementData;
// 當前元素個數
private int size;
// 數組最大容量限制(防止溢出)
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

關鍵點

  • elementData:實際存儲數據的數組,初始化為DEFAULTCAPACITY_EMPTY_ELEMENTDATA(空數組),首次添加元素時擴容到默認容量 10
  • size:記錄當前集合中實際元素的數量,與 elementData.length 不同。
  • transientelementData 被標記為 transient,但 ArrayList 自定義了 writeObjectreadObject 方法,實現序列化邏輯
  • MAX_ARRAY_SIZE:限制數組最大容量為 Integer.MAX_VALUE - 8,避免 JVM 內存分配異常

.

二、構造方法詳解

1. 無參構造函數

public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
  • 初始化為空數組 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
  • 首次添加元素時,會擴容到默認容量 10

2. 帶初始容量的構造函數

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);}
}
  • 直接初始化指定容量的數組,避免后續頻繁擴容

3. 使用集合初始化

public ArrayList(Collection<? extends E> c) {elementData = c.toArray();if ((size = elementData.length) != 0) {if (elementData.getClass() != Object[].class)elementData = Arrays.copyOf(elementData, size, Object[].class);} else {this.elementData = EMPTY_ELEMENTDATA;}
}
  • 將集合轉換為數組并賦值給 elementData
  • 如果集合返回的數組類型不是 Object[],會強制轉換(避免類型問題)

.

三、核心方法源碼分析

1. 添加元素:add(E e)

public boolean add(E e) {modCount++; // 修改次數 +1(用于迭代器 fail-fast)add(e, elementData, size);return true;
}private void add(E e, Object[] elementData, int s) {if (s == elementData.length)elementData = grow(); // 擴容elementData[s] = e;size = s + 1;
}
  • 關鍵步驟:
    1. 檢查是否需要擴容(s == elementData.length)
    2. 調用 grow() 方法擴容
    3. 將元素賦值到 elementData[s]
    4. 更新 size

2. 擴容機制:grow()

private Object[] grow() {return grow(size + 1);
}private Object[] grow(int minCapacity) {int oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1);     // 1.5 倍擴容if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);return elementData = Arrays.copyOf(elementData, newCapacity);
}
  • 擴容規則:
    • 默認擴容:newCapacity = oldCapacity + (oldCapacity >> 1)(即 1.5 倍)
    • 特殊情況:如果擴容后仍不足(如一次性添加大量元素),直接設置為 minCapacity
    • 最大容量限制:超過 MAX_ARRAY_SIZE 時調用 hugeCapacity() 處理

hugeCapacity(int minCapacity)

private static int hugeCapacity(int minCapacity) {if (minCapacity < 0)throw new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
  • 如果 minCapacity 超過 MAX_ARRAY_SIZE,則返回 Integer.MAX_VALUE,否則返回 MAX_ARRAY_SIZE

3. 刪除元素:remove(int index)

public E remove(int index) {Objects.checkIndex(index, size); // 檢查索引合法性final Object[] es = elementData;@SuppressWarnings("unchecked") E oldValue = (E) es[index];int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(es, index+1, es, index, numMoved); // 左移元素es[--size] = null; // 幫助 GCreturn oldValue;
}
  • 關鍵步驟:
    1. 檢查索引合法性
    2. 獲取目標元素
    3. 將右半部分元素左移(時間復雜度 O(n))
    4. 將最后一個元素置為 null,幫助垃圾回收
    5. 更新 size

4. 獲取元素:get(int index)

public E get(int index) {Objects.checkIndex(index, size);return (E) elementData[index];
}
  • 特點:直接通過索引訪問數組元素,時間復雜度 O(1)

5. 修改元素:set(int index, E element)

public E set(int index, E element) {Objects.checkIndex(index, size);E oldValue = (E) elementData[index];elementData[index] = element;return oldValue;
}
  • 特點:直接修改數組中指定位置的元素,時間復雜度 O(1)

.

四、性能優化技巧

1. 預估容量,避免頻繁擴容

使用 ArrayList(int initialCapacity) 構造函數,提前指定容量。如果容量不足,頻繁擴容會導致性能下降(每次擴容需復制數組)

2. 手動擴容:ensureCapacity(int minCapacity)

public void ensureCapacity(int minCapacity) {int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)? 0 : DEFAULT_CAPACITY;if (minCapacity > minExpand) {modCount++;grow(minCapacity);}
}
  • 在添加大量元素前,調用此方法預擴容,減少中間擴容次數

3. 清空集合:clear()

public void clear() {modCount++;for (int i = 0; i < size; i++)elementData[i] = null; // 幫助 GCsize = 0;
}
  • 注意:clear() 不會釋放數組空間,只是將 size 置為 0 并將元素設為 null

.

五、線程安全問題

1. 線程不安全

ArrayList 不是線程安全的,多線程環境下可能引發數據不一致或 ConcurrentModificationException

2. 解決方案

  • 使用 Collections.synchronizedList
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
  • 使用 CopyOnWriteArrayList(適合讀多寫少場景):
List<String> cowList = new CopyOnWriteArrayList<>();

.

六、Fail-Fast 機制

1. 原理

  • ArrayList 通過 modCount 變量記錄集合的修改次數
  • 迭代器遍歷時會檢查 modCount 是否與創建迭代器時的值一致。如果不一致,拋出 ConcurrentModificationException

2. 代碼示例

public Iterator<E> iterator() {return new Itr();
}private class Itr implements Iterator<E> {int expectedModCount = modCount;public boolean hasNext() {checkForComodification();return cursor != size;}final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}
}

.

七、性能對比與使用建議

在這里插入圖片描述

使用建議

  • 高頻隨機訪問:優先使用 ArrayList
  • 頻繁中間插入/刪除:使用 LinkedList
  • 線程安全:使用 Collections.synchronizedListCopyOnWriteArrayList

八、其他實用方法

暫時無法在飛書文檔外展示此內容

九、總結

暫時無法在飛書文檔外展示此內容

十、擴展學習建議

  1. 源碼調試:使用 IDE(如 IntelliJ IDEA)逐步調試 ArrayList 的 addremove 等方法,觀察 elementDatasize 的變化
  2. 版本差異:對比 JDK 8 和 JDK 17 的 ArrayList 源碼,觀察擴容策略、subList 實現等細節變化
  3. 進階主題
  • ArrayListVector 的區別(線程安全 vs 性能)
  • subList 返回的視圖對原集合的影響
  • ArrayList 在 JVM 內存模型中的表現(數組的連續性 vs 鏈表的離散性)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/84760.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/84760.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/84760.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Qt】Qt控件

文章目錄 Qt控件Layout Spacer垂直布局QVBoxLayout水平排列布局QHBoxLayout網格布局 QGridLayout表格布局 QFormLayout Button Contain命令按鈕Push Button工具按鈕Tool Button單選按鈕Radio Button復選框按鈕Check Box命令鏈接按鈕Command Link Button按鈕盒Button Box組合框G…

PHP基礎-運算符

PHP 的運算符是編程中非常基礎但又非常重要的一部分&#xff0c;掌握它們能讓你更靈活地處理各種邏輯、計算和流程控制。 算術運算符 用于基本數學運算&#xff1a; 運算符含義示例加法$a $b-減法$a - $b*乘法$a * $b/除法$a / $b%取模$a % $b 示例&#xff1a; <?ph…

AR珠寶佩戴與傳統的珠寶購物有哪些區別??

AR 珠寶佩戴與傳統的珠寶購物究竟存在著哪些顯著區別呢?在傳統的珠寶購物模式里&#xff0c;顧客往往需要花費時間和精力前往實體珠寶店。踏入店內&#xff0c;首先映入眼簾的便是那一排排的玻璃展柜&#xff0c;此時&#xff0c;銷售人員會熱情地走上前&#xff0c;小心翼翼地…

華為云CAE部署spring cloud服務

1 概述 華為云CAE&#xff08;Cloud Application Engine云應用引擎&#xff09;是一個面向WEB、微服務應用的Serverless托管服務&#xff0c;提供極速部署、極低成本、極簡運維的一站式應用托管方案。支持從源碼、軟件包、鏡像包快速發布應用&#xff0c;秒級彈性伸縮、按量付…

【技術工具】源碼管理 - GIT工具

【技術工具】源碼管理 - GIT工具 1 前言 之前參考語雀一位大佬的&#xff0c;但鏈接找不到了&#xff0c;僅供參考。 1、檢查空白錯誤 //確認將提交的內容中有無空白信息 git diff --check 2、嘗試讓每一個提交成為一個邏輯的獨立變更集 盡量使每筆提交都成為獨立的patch&a…

Objective-c Block 面試題

以下是對我們這整段關于 Objective-C 中 Block、__block 修飾符、內存管理行為、生命周期等內容的全面總結&#xff0c;并附帶了一套適合面試準備的面試題集&#xff08;帶答案&#xff09;。 &#x1f9e0; 一、知識總結&#xff1a;Objective-C Block __block 修飾符 ? Bl…

AndroidMJ-基礎-05

基礎part5: 9:測試相關 postman genemotion espresso 10:性能相關 profiler 9.測試相關 espresso相關&#xff1a; Android Espresso 自動化測試指南&#xff08;Java 版&#xff09;-CSDN博客 10.性能相關 profiler相關&#xff1a; AndroidStudio之內層泄漏工具Profiler…

R語言 | 如何使用R書寫html文檔?

更靈活的書寫方式&#xff0c;可以直接看3. 1. 可用函數 cat()函數writeLines()函數sink()函數重定向輸出到HTML文件 小結&#xff1a;cat()適合簡單HTML&#xff0c;writeLines()適合多行內容&#xff0c;sink()適合復雜場景。 說明&#xff1a;盡可能不用R包&#xff0c;減…

oracle 表空間超過最大限度,清理數據釋放內存

目錄 一、擴容&#xff1a;參考 https://blog.csdn.net/weixin_40841731/article/details/134931289 二、清理數據 1、查詢文件大小情況&#xff08;管理員賬號&#xff09; 2、查詢表的大小&#xff08;使用該表空間的用戶&#xff09; 3、清理數據&#xff08;使用該表空…

初版BL程序一些細節整理(碎碎念)

一.串口的中斷觸發 一般我們都是使用TXE或者RXNE來觸發中斷&#xff0c;其實還有完整傳輸結束的TC標志位和接收完成的IDLE標志位 這兩個標志位有些不同&#xff0c;RXNE標志位只需要讀取寄存器就會自行清除&#xff0c;但是這兩個需要讀取兩個&#xff0c;拿IDLE舉例子 這里需要…

為何京東與螞蟻集團競相申請穩定幣牌照?

京東與螞蟻集團競相申請穩定幣牌照&#xff0c;主要是為了搶占數字金融新賽道&#xff0c;結合香港的寬松監管政策與全球穩定幣市場的快速增長。香港2023年推出的穩定幣監管框架及2025年8月即將實施的《穩定幣條例》&#xff0c;為企業提供了合規路徑&#xff0c;吸引京東通過幣…

[特殊字符] Harmony OS Next里的Web組件:網頁加載的全流程掌控手冊

&#x1f389; Harmony OS Next里的Web組件&#xff1a;網頁加載的全流程掌控手冊 ##Harmony OS Next ##Ark Ts ##教育 本文適用于教育科普行業進行學習&#xff0c;有錯誤之處請指出我會修改。 開發者必看的生命周期回調詳解代碼實操指南 作為開發者&#xff0c;你可能經常需…

【Java學習筆記】集合介紹

集合 > > 集合的引出 在之前常使用數組存儲數據&#xff0c;存在的問題如下&#xff1a; &#xff08;1&#xff09;初始化時&#xff0c;長度必須指定&#xff0c;而且一旦指定&#xff0c;不能更改 &#xff08;2&#xff09;不方便擴容&#xff08;使用循環復制原…

電流傳感器在汽車中的應用:從BMS電池管理到電機控制的工程解析

1 電流傳感器&#xff1a;汽車電子系統的神經末梢 在現代汽車電子架構中&#xff0c;電流傳感器已從簡單的測量元件演變為??關鍵的安全與性能組件??。作為動力系統的“神經末梢”&#xff0c;它們持續采集電流參數并反饋至控制單元&#xff0c;構成??實時閉環控制的基礎…

積分商城拼團系統框架設計

一、邏輯分析 用戶相關邏輯 用戶注冊與登錄&#xff1a;用戶需要注冊賬號才能參與積分商城拼團活動。注冊過程中需收集必要信息&#xff0c;如用戶名、密碼、聯系方式等。登錄功能則用于驗證用戶身份&#xff0c;方便用戶后續操作。用戶積分管理&#xff1a;用戶通過各種途徑&a…

java 數據結構-HashMap

一、hashmap特點 1、HashMap 是一個散列表,它存儲的內容是鍵值對(key-value)映射。 2、HashMap 實現了 Map 接口,根據鍵的 HashCode 值存儲數據,具有很快的訪問速度,最多允許一條記錄的鍵為 null,不支持線程同步。 3、HashMap 是無序的,即不會記錄插入的順序。 4、HashMa…

DBSyncer:一款開源的數據同步工具

DBSyncer&#xff08;簡稱 dbs&#xff09;是一款開源的實時數據同步中間件&#xff0c;提供 MySQL、Oracle、SQL Server、PostgreSQL、SQLite、Elasticsearch、Kafka、File、SQL 數據庫等同步場景&#xff1b;支持上傳插件自定義同步轉換業務&#xff1b;提供監控全量和增量數…

大型語言模型的中毒攻擊的系統評價

大家讀完覺得有幫助記得及時關注和點贊&#xff01;&#xff01;&#xff01; 抽象 隨著預訓練大型語言模型 &#xff08;LLM&#xff09; 及其訓練數據集的廣泛使用&#xff0c;人們對與其使用相關的安全風險的擔憂顯著增加。 這些安全風險之一是 LLM 中毒攻擊的威脅&#xff…

Windows 10更新失敗解決方法

前言 在我們使用 Windows 時的時候&#xff0c;很多時候遇到系統更新 重啟之后卻一直提示“我們無法完成更新&#xff0c;正在撤銷更改” 這種情況非常煩人&#xff0c;但其實可以通過修改文件的方法解決&#xff0c;并且正常更新到最新版操作系統 01修改注冊表 管理員身份…

Redis高級|Redis單線程VS多線程(基礎)

文章目錄 面試題Redis為什么選擇單線程為什么逐漸加入多線程特性Redis6、Redis7的多線程特性和IO多路復用入門Redis7多線程 面試題 Redis到底是單線程還是多線程&#xff1f;IO多路復用聽說過嗎&#xff1f;Redis為什么這么快&#xff1f; Redis為什么選擇單線程 其實Redis單…