java-arraylist 源碼分析 1

## 深入分析 Java 中的 `ArrayList` 源碼

`ArrayList` 是 Java 集合框架中的一個重要類,它基于數組實現,提供了動態數組的功能。`ArrayList` 是一個非常常用的集合類,因為它在隨機訪問和遍歷方面性能優越。本文將詳細分析 `ArrayList` 的源碼,包括其數據結構、構造方法、核心操作、自動擴容機制等。

### 1. `ArrayList` 的基本數據結構

`ArrayList` 是一個動態數組,它的底層是一個 `Object` 類型的數組。在 `ArrayList` 的實現中,主要使用以下幾個關鍵字段:

```java
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
? ? private static final long serialVersionUID = 8683452581122892189L;

? ? // 默認初始容量
? ? private static final int DEFAULT_CAPACITY = 10;

? ? // 空數組實例,當初始容量為0時使用
? ? private static final Object[] EMPTY_ELEMENTDATA = {};

? ? // 默認空數組實例,當使用默認構造函數時使用
? ? private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

? ? // 存儲元素的數組
? ? transient Object[] elementData;

? ? // 元素數量
? ? private int size;
}
```

- `elementData`:這是實際存儲元素的數組。
- `size`:這是當前 `ArrayList` 中包含的元素數量。
- `DEFAULT_CAPACITY`:這是默認的初始容量。
- `EMPTY_ELEMENTDATA` 和 `DEFAULTCAPACITY_EMPTY_ELEMENTDATA`:分別是用于初始化時的空數組。

### 2. 構造方法

`ArrayList` 提供了多個構造方法:

#### 2.1 默認構造方法

```java
public ArrayList() {
? ? this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
```

默認構造方法將 `elementData` 初始化為 `DEFAULTCAPACITY_EMPTY_ELEMENTDATA`,即一個空數組。當第一次添加元素時,數組將擴展到默認初始容量。

#### 2.2 指定初始容量的構造方法

```java
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);
? ? }
}
```

此構造方法允許用戶指定 `ArrayList` 的初始容量。如果初始容量大于 0,則創建一個相應大小的數組;如果初始容量為 0,則使用空數組;如果初始容量為負數,則拋出 `IllegalArgumentException`。

#### 2.3 從另一個集合創建 `ArrayList`

```java
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;
? ? }
}
```

此構造方法從另一個集合創建 `ArrayList`,并將集合中的所有元素添加到 `ArrayList` 中。

### 3. 核心操作方法

#### 3.1 添加元素

添加元素的方法主要有 `add(E e)` 和 `add(int index, E element)`:

```java
public boolean add(E e) {
? ? ensureCapacityInternal(size + 1); ?// 擴容檢查
? ? elementData[size++] = e;
? ? return true;
}

public void add(int index, E element) {
? ? rangeCheckForAdd(index);

? ? ensureCapacityInternal(size + 1); ?// 擴容檢查
? ? System.arraycopy(elementData, index, elementData, index + 1, size - index);
? ? elementData[index] = element;
? ? size++;
}
```

- `add(E e)`:在數組末尾添加元素,需要先檢查容量是否足夠,如果不足,則進行擴容。
- `add(int index, E element)`:在指定位置插入元素,需要先檢查索引的有效性,然后將指定位置后的元素向后移動,再插入新元素。

#### 3.2 確保容量

`ensureCapacityInternal(int minCapacity)` 方法用于確保數組有足夠的容量,如果不足則進行擴容:

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

### 4. 自動擴容機制

當 `ArrayList` 中的元素超過當前數組的容量時,需要進行擴容。`grow(int minCapacity)` 方法用于擴容:

```java
private void grow(int minCapacity) {
? ? int oldCapacity = elementData.length;
? ? int newCapacity = oldCapacity + (oldCapacity >> 1);
? ? if (newCapacity - minCapacity < 0)
? ? ? ? newCapacity = minCapacity;
? ? if (newCapacity - MAX_ARRAY_SIZE > 0)
? ? ? ? newCapacity = hugeCapacity(minCapacity);
? ? 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;
}
```

- `oldCapacity`:舊容量,即擴容前數組的長度。
- `newCapacity`:新容量,為舊容量的 1.5 倍。
- 如果 `newCapacity` 仍小于所需的最小容量,則將其設為 `minCapacity`。
- 如果 `newCapacity` 超過了 `MAX_ARRAY_SIZE`,則調用 `hugeCapacity(int minCapacity)` 方法進行處理。
- 最后通過 `Arrays.copyOf` 方法將舊數組復制到新數組中。

### 5. 刪除元素

`ArrayList` 提供了刪除元素的方法 `remove(int index)` 和 `remove(Object o)`:

```java
public E remove(int index) {
? ? rangeCheck(index);

? ? modCount++;
? ? E oldValue = elementData(index);

? ? 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

? ? return oldValue;
}

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

private void fastRemove(int index) {
? ? 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
}
```

- `remove(int index)`:根據索引刪除元素,首先檢查索引的有效性,然后通過 `System.arraycopy` 方法將索引后的元素向前移動,覆蓋要刪除的元素。
- `remove(Object o)`:根據對象刪除元素,通過遍歷找到目標元素并調用 `fastRemove(int index)` 方法進行刪除。

### 6. 獲取元素和修改元素

`ArrayList` 提供了獲取元素的方法 `get(int index)` 和修改元素的方法 `set(int index, E element)`:

```java
public E get(int index) {
? ? rangeCheck(index);
? ? return elementData(index);
}

public E set(int index, E element) {
? ? rangeCheck(index);

? ? E oldValue = elementData(index);
? ? elementData[index] = element;
? ? return oldValue;
}
```

- `get(int index)`:根據索引獲取元素,首先檢查索引的有效性,然后返回數組中的對應元素。
- `set(int index, E element)`:根據索引修改元素,首先檢查索引的有效性,然后將指定位置的元素替換為新元素,并返回舊元素。

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

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

相關文章

spring cloud gateway客戶端websocket斷開連接,服務側連接沒有關閉的問題處理

之前在單體架構項目中使用了websocket主動推送消息的功能&#xff0c;后來改成了微服務架構&#xff0c;結果發現部分消息丟失&#xff0c;沒能推送給客戶端&#xff1b;深入排查發現服務端無法感知websocket連接狀態&#xff0c;但是在單體架構里面是沒這個問題的&#xff0c;…

Redis【超詳細】

Redis 是一個基于內存的key-value結構的數據庫 一、redis的安裝 1.1、安裝步驟 1&#xff09;安裝Redis依賴 Redis是基于c語言編寫的&#xff0c;因此需要安裝對應的gcc環境 yum install -y gcc tcl 2&#xff09;進入/usr/local/src/目錄上傳并解壓安裝包 解壓&#xf…

【APK】SDKManager運行后閃退

本地JDK已安裝&#xff0c;且配置了環境變量&#xff0c;未安裝 android studiio 問題描述&#xff1a;右鍵以管理員身份運行 SDKManager&#xff0c;終端窗口閃退 問題原因&#xff1a;未找到正確的Java路徑 解決辦法&#xff1a; 1.修改tools目錄下的 android.bat 文件&am…

langchain 入門中篇:數據封裝,Memory 封裝

數據的處理流程可以看一張圖來幫助理解 數據來源可以是網絡&#xff0c;可以是郵件&#xff0c;可以是本地文件 經過 Document Loaders 加載&#xff0c;再在 Transform 階段對文檔進行 split, filter, translate, extract metadata 等操作&#xff0c;之后在 Embed 階段進行向…

Keil用ST-LINK下載STM32程序后不自動運行

之后程序可以運行了&#xff0c;但是串口還沒有輸出&#xff0c;在debug模式下都是ok的。

加權 KNN 算法的原理與詳解

加權kNN&#xff0c;k近鄰算法的增強改進版本。 加權KNN算法 近鄰算法&#xff08;k-Nearest Neighbors, kNN&#xff09;是一種用于分類和回歸的非參數方法。它的基本思想是“看鄰居”&#xff0c;即通過查找離目標點最近的 K 個數據點&#xff0c;來判斷目標點的類別或數值。…

docker安裝elasticesarch-head

安裝 Elasticsearch-Head 通常涉及以下步驟&#xff1a; 拉取 Elasticsearch-Head 的 Docker 鏡像。 運行 Elasticsearch-Head 容器并連接到 Elasticsearch 實例。 以下是具體的命令&#xff1a; 拉取 Elasticsearch-Head 的 Docker 鏡像 docker pull mobz/elasticsearch-…

Sqlserver 如何創建全局只讀賬號?

由于SQL Server不支持全局數據庫權限&#xff0c;因此需要在每個數據庫中創建用戶并授予其只讀權限。可以使用動態SQL腳本來為所有現有數據庫設置權限&#xff0c;具體腳本如下 ##創建登陸賬號CREATE LOGIN user01 WITH PASSWORD password; ##除了系統庫外給user01 db_datare…

FactoryBean原理及用法

它的作用是用制造創建過程較為復雜的產品, 如 SqlSessionFactory, 但 Bean 已具備等價功能 使用 被 FactoryBean 創建的產品 會認為創建、依賴注入、Aware 接口回調、前初始化這些都是 FactoryBean 的職責, 這些流程都不會走 唯有后初始化的流程會走, 也就是產品可以被代理增…

學習aurora64/66b.20240703

簡介 The AMD LogiCORE?IP Aurora 64B/66B core是一種可擴展的輕量級高數據速率鏈路層協議&#xff0c;用于高速串行通信。該協議是開放的&#xff0c;可以使用AMD設備技術實現。 Aurora 64B/66B是一種輕量級的串行通信協議&#xff0c;適用于多千兆位鏈路 (如下圖所示)。它…

【MATLAB源碼-第139期】基于matlab的OFDM信號識別與相關參數的估計,高階累量/小波算法調制識別,循環譜估計,帶寬估計,載波數目估計等等。

操作環境&#xff1a; MATLAB 2022a 1、算法描述 在現代無線通信系統中&#xff0c;正交頻分復用&#xff08;OFDM&#xff09;因其高效的頻譜利用率、強大的抗多徑衰落能力以及靈活的帶寬分配等優勢&#xff0c;成為了一種非常重要的調制技術。然而&#xff0c;隨著無線通信…

采沙船智能監測識別攝像機

對于現代河流管理來說&#xff0c;采沙船智能監測識別攝像機正逐漸成為解決非法采砂和保護河流生態環境的重要工具。這類攝像機通過先進的視覺識別和數據分析技術&#xff0c;有效監控和管理河道上的采沙行為&#xff0c;對保護水域資源和改善生態環境具有顯著的意義。 采沙船智…

Linux容器篇-使用kubeadm搭建一個kubernetes集群

kubernetes集群架構和組件 master節點組件 kube-apiserver&#xff1a;Kubernetes API&#xff0c;集群的統一入口&#xff0c;各組件的協調者&#xff0c;以RESTful API提供接口服務&#xff0c;所有對象資源的增刪改查和監聽操作都交給APIserver處理后再交給Etcd存儲。 kube…

學習Mybatis

Mybatis 第一節 引言 1. 什么是框架 框架是一個半成品&#xff0c;解決了軟件開發過程中的普遍性問題&#xff0c;簡化了開發步驟&#xff0c;提高了開發效率。 2. 什么是ORM ORM全稱為Object Relational Mapping&#xff0c;意為對象關系映射&#xff0c;主要實現了將程序…

usecallback()與usememo()

簡單的說 都是用來監聽數據變化 來進行控制渲染、減少不必要的渲染 、優化性能 usecallback()是用來監聽數據變化從而調用方法 usememo()是用來監聽數據變化從而改變數據 使用return返回變化的數據 當然return 也可以返回方法 所以usememo()可以代替usecallback() 下面詳解 …

常見的編碼技術簡介

常見的編碼技術簡介 文章目錄 常見的編碼技術簡介1. 字符編碼1.1 ASCII1.2 Unicode 2. 數據傳輸編碼2.1 Base系列編碼2.1.1 Base642.1.2 Base162.1.3 Base322.1.4 Base852.1.5 其他Base編碼 2.2 URL編碼2.3 JSON2.4 XML2.5 Protobuf (Protocol Buffers) 1. 字符編碼 1.1 ASCII…

AI是在幫助開發者還是取代他們?——探討AI在軟件開發中的角色與未來

引言 隨著人工智能技術的迅猛發展&#xff0c;AI工具在軟件開發中的應用越來越廣泛。有人認為AI可以顯著提升開發者的效率&#xff0c;而也有人擔心AI會取代開發者的工作。本文將從三個方面探討AI在軟件開發中的角色&#xff1a;AI工具現狀、AI對開發者的影響以及AI開發的未來…

學習springAOP

第三章 Spring AOP 第一節 AOP 簡介 1. 概念 AOP全稱為Aspect Oriented Programming&#xff0c;表示面向切面編程。何為切面呢&#xff1f; 由此可以得出&#xff0c;切面是一種將那些與業務無關&#xff0c;但業務模塊都需要使用的功能封裝起來的技術。這樣便于減少系統的…

昇思25天學習打卡營第4天|應用實踐

昇思25天學習打卡營第4天 文章目錄 昇思25天學習打卡營第4天基于 MindSpore 實現 BERT 對話情緒識別模型簡介環境配置數據集數據加載和數據預處理input_idsattention_mask 模型構建模型驗證模型推理自定義推理數據集 打卡記錄 基于 MindSpore 實現 BERT 對話情緒識別 模型簡介…

奧比中光astra_pro相機使用記錄

一、信息獲取 1、官網 用于了解產品信息 http://www.orbbec.com.cn/sys/37.html 2、開發者社區 咨詢問題下載開發部https://developer.orbbec.com.cn/ 二 、windowvs19 1、相機型號 orbbec_astro_pro 根據對應的型號找到需要的包工具 踩坑1&#xff0c;因為這個相機型號…