深入淺出 ArrayList:從基礎用法到底層原理的全面解析(中)

四、ArrayList 常用方法實戰 —— 從添加到遍歷的全場景覆蓋

ArrayList 提供了數十個方法,但日常開發中常用的只有 10 個左右,我們按 “元素操作”“集合查詢”“遍歷方式” 三類來梳理,每個方法都附帶示例和注意事項。

4.1 元素添加:add () 與 addAll ()

添加元素是 ArrayList 最基礎的操作,核心方法有兩個:add(E e)(添加單個元素到尾部)和addAll(Collection<? extends E> c)(添加另一個集合的所有元素到尾部)。

4.1.1 add (E e):尾部添加單個元素

源碼核心邏輯:先檢查是否需要擴容,再將元素添加到elementData[size],最后size++

public boolean add(E e) {// 確保容量足夠(size+1:因為要添加1個元素)ensureCapacityInternal(size + 1);// 將元素添加到數組的size位置(此時size是當前元素個數,索引從0開始)elementData[size++] = e;return true;
}

示例:

List<Integer> list = new ArrayList<>();
list.add(10); // 添加成功,返回true
list.add(20);
System.out.println(list); // 輸出:[10, 20]
System.out.println(list.size()); // 輸出:2(元素個數)

注意:add()方法永遠返回true(因為 ArrayList 允許添加任意元素,不會因 “元素已存在” 而返回 false,這與 Set 的add()不同)。

4.1.2 add (int index, E element):指定索引插入元素

除了尾部添加,還可以在指定索引位置插入元素,但需要注意:插入位置會導致后續元素 “向后移動”,時間復雜度為 O (n),頻繁使用會影響性能。

示例:

List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
// 在索引1的位置插入"c"(原索引1的"b"向后移動到索引2)
list.add(1, "c");
System.out.println(list); // 輸出:[a, c, b]

注意:如果指定的索引index超出[0, size]范圍(即index < 0index > size),會拋出IndexOutOfBoundsException(索引越界異常)。

4.1.3 addAll (Collection<? extends E> c):添加另一個集合的所有元素

示例:

List<String> list1 = new ArrayList<>();
list1.add("a");
list1.add("b");List<String> list2 = new ArrayList<>();
list2.add("c");
list2.add("d");// 將list2的所有元素添加到list1尾部
list1.addAll(list2);
System.out.println(list1); // 輸出:[a, b, c, d]

addAll()返回boolean:如果集合因添加操作發生了變化(即 c 不為空),返回true;如果 c 為空,返回false

4.2 元素刪除:remove () 與 clear ()

刪除元素是 ArrayList 的高頻操作,但需要注意 “刪除指定索引” 和 “刪除指定元素” 的區別,以及刪除后元素的移動問題。

4.2.1 remove (int index):刪除指定索引的元素

源碼核心邏輯:先檢查索引是否合法,再計算需要移動的元素個數,通過System.arraycopy將后續元素向前移動 1 位,最后將數組末尾的元素置為 null(幫助 GC 回收)。

public E remove(int index) {// 檢查索引是否越界(index >= size則拋出異常)rangeCheck(index);modCount++;// 獲取要刪除的元素(用于返回)E oldValue = elementData(index);// 計算需要移動的元素個數:size - index - 1(比如刪除索引2,size=5,需要移動5-2-1=2個元素)int numMoved = size - index - 1;if (numMoved > 0) {// 將elementData中index+1到size-1的元素,復制到index到size-2的位置(向前移動1位)System.arraycopy(elementData, index+1, elementData, index, numMoved);}// 將數組末尾的元素置為null(幫助GC回收,避免內存泄漏)elementData[--size] = null;// 返回被刪除的元素return oldValue;
}

示例:

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
// 刪除索引2的元素(即"c")
String removed = list.remove(2);
System.out.println("被刪除的元素:" + removed); // 輸出:被刪除的元素:c
System.out.println(list); // 輸出:[a, b, d]

注意:remove(int index)返回的是 “被刪除的元素”,而不是boolean,這一點容易和remove(Object o)混淆。

4.2.2 remove (Object o):刪除指定元素(第一個匹配項)

remove(int index)不同,remove(Object o)是根據 “元素值” 刪除,且只刪除 “第一個匹配的元素”(如果元素重復)。

源碼核心邏輯:先判斷 o 是否為 null(null 元素用 == 判斷,非 null 元素用 equals () 判斷),找到第一個匹配的索引后,調用fastRemove()刪除(邏輯與remove(int index)一致)。

public boolean remove(Object o) {if (o == null) {// 遍歷數組,找到第一個null元素for (int index = 0; index < size; index++)if (elementData[index] == null) {fastRemove(index);return true;}} else {// 遍歷數組,找到第一個equals(o)為true的元素for (int index = 0; index < size; index++)if (o.equals(elementData[index])) {fastRemove(index);return true;}}// 沒找到元素,返回falsereturn 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;
}

示例:

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "b"));
// 刪除第一個"b"
boolean isRemoved = list.remove("b");
System.out.println("是否刪除成功:" + isRemoved); // 輸出:true
System.out.println(list); // 輸出:[a, c, b](只刪除了第一個"b")

注意:

  1. 如果集合中存在多個相同元素,remove(Object o)只刪除第一個;
  2. 如果存儲的是自定義對象,必須重寫equals()方法,否則無法正確匹配元素(默認用==判斷地址,而非內容)。
4.2.3 clear ():清空所有元素

clear()會刪除集合中的所有元素,但不會清空數組容量,只是將數組中的元素置為 null,幫助 GC 回收,size置為 0。

源碼:

public void clear() {modCount++;// 將所有元素置為null(幫助GC)for (int i = 0; i < size; i++)elementData[i] = null;// 元素個數置為0size = 0;
}

示例:

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
list.clear();
System.out.println(list); // 輸出:[](空集合)
System.out.println(list.size()); // 輸出:0

如果需要 “清空元素并縮小數組容量”,可以在clear()后調用trimToSize()(后文會講)。

4.3 元素查詢與修改:get ()、set ()、contains ()

4.3.1 get (int index):獲取指定索引的元素

get()是 ArrayList 的核心查詢方法,基于索引隨機訪問,時間復雜度 O (1),源碼:

public E get(int index) {// 檢查索引是否越界rangeCheck(index);// 返回數組中index位置的元素return elementData(index);
}// 索引越界檢查
private void rangeCheck(int index) {if (index >= size)throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

示例:

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
String element = list.get(1);
System.out.println(element); // 輸出:b

注意:index必須在[0, size-1]范圍內,否則拋出IndexOutOfBoundsException(比如list.get(3),size=3,索引最大為 2,會報錯)。

4.3.2 set (int index, E element):修改指定索引的元素

set()用于替換指定索引的元素,返回 “被替換的舊元素”,源碼:

public E set(int index, E element) {rangeCheck(index); // 索引越界檢查E oldValue = elementData(index); // 獲取舊元素elementData[index] = element; // 替換為新元素return oldValue; // 返回舊元素
}

示例:

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
// 將索引1的元素改為"d"
String oldElement = list.set(1, "d");
System.out.println("被替換的舊元素:" + oldElement); // 輸出:b
System.out.println(list); // 輸出:[a, d, c]
4.3.3 contains (Object o):判斷元素是否存在

contains()用于判斷集合中是否包含指定元素,返回boolean,底層通過indexOf()實現:

public boolean contains(Object o) {return indexOf(o) >= 0;
}// 查找元素的第一個索引,不存在返回-1
public int indexOf(Object o) {if (o == null) {for (int i = 0; i < size; i++)if (elementData[i] == null)return i;} else {for (int i = 0; i < size; i++)if (o.equals(elementData[i]))return i;}return -1;
}

示例:

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
boolean hasB = list.contains("b");
boolean hasD = list.contains("d");
System.out.println("是否包含b:" + hasB); // 輸出:true
System.out.println("是否包含d:" + hasD); // 輸出:false

注意:contains()的時間復雜度是 O (n)(需要遍歷數組),如果集合元素較多,頻繁調用會影響性能。

4.4 ArrayList 的 4 種遍歷方式

遍歷是集合操作的高頻場景,ArrayList 支持多種遍歷方式,不同方式的效率和適用場景不同,需根據需求選擇。

4.4.1 普通 for 循環(基于索引)

適合需要 “獲取索引” 的場景,效率較高(直接隨機訪問):

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
for (int i = 0; i < list.size(); i++) {System.out.println("索引" + i + ":" + list.get(i));
}

輸出:

索引0:a
索引1:b
索引2:c
4.4.2 增強 for 循環(foreach)

語法簡潔,無需關心索引,適合 “只遍歷元素,不關心索引” 的場景:

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
for (String element : list) {System.out.println(element);
}
a
b
c

注意:foreach 底層是通過迭代器(Iterator)實現的,遍歷過程中不能修改集合結構(如 add/remove),否則會拋出ConcurrentModificationException

4.4.3 迭代器(Iterator)

適合 “需要在遍歷過程中刪除元素” 的場景(必須用迭代器的remove()方法,而非集合的remove()):

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {String element = iterator.next();if ("b".equals(element)) {// 用迭代器的remove()刪除,不會拋出異常iterator.remove();}System.out.println(element);
}
System.out.println("刪除后的集合:" + list); // 輸出:[a, c]

注意:

  1. iterator.next()必須在iterator.hasNext()之后調用,否則會拋出NoSuchElementException
  2. 遍歷過程中,只能用iterator.remove()刪除元素,不能用list.remove(),否則會破壞迭代器的modCountexpectedModCount一致性,導致異常。
4.4.4 Stream 流遍歷(JDK 1.8+)

適合 “需要對元素進行過濾、映射等復雜操作” 的場景,語法簡潔,支持鏈式調用:

List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
// 過濾出長度為1的元素,轉為大寫后遍歷輸出
list.stream().filter(element -> element.length() == 1).map(String::toUpperCase).forEach(System.out::println);

輸出:

A
B
C
D

Stream 流遍歷的優勢在于 “功能性編程”,能快速實現復雜的元素處理邏輯,是 JDK 1.8 + 后的推薦方式之一。

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

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

相關文章

java后端如何實現下載功能

后端需要把要下載的若干文件 按 ZIP 格式編碼成一段二進制字節流&#xff0c;然后以 Content-Type: application/zip Content-Disposition: attachment; filenamexxx.zip 的形式寫進 HTTP 響應體。瀏覽器收到這段“ZIP 格式的字節流”后&#xff0c;就會彈出保存對話框&#xf…

AI生成技術報告:GaussDB與openGauss的HTAP功能全面對比

GaussDB 與 openGauss 的 HTAP 功能比較 前言 GaussDB集中式版本從505.2版本開始引入了HTAP混合負載功能&#xff0c;openGauss也從7.0.0 RC1版本開始引入了HTAP行列融合功能&#xff0c;加強了行存轉列存的使用友好度&#xff0c;但兩者的實現似乎存在不小的差異。 雖然文檔…

小程序開發指南(四)(UI 框架整合)

?講解了微信小程序 UI 框架的使用方法和特點&#xff0c;根據項目需求選擇合適的組件庫。附有相應的組件庫預覽碼&#xff0c;也是將所有的微信小程序原生組件庫整合在一起方便后續開發的使用。如果有不好或者有錯誤的地方請告知&#xff01;希望可以與大家相互的交流學習&…

golang 1.25.0 安裝

wget https://golang.google.cn/dl/go1.25.0.linux-amd64.tar.gz tar -C /usr/local/ -xzf go1.25.0.linux-amd64.tar.gz ln -s /usr/local/go/bin/* /usr/bin/ go env -w GO111MODULEon go env -w GOPROXYhttps://goproxy.cn,direct

基于深度學習的人臉表情識別系統:YOLOv5/v6/v7/v8/v10模型實現與UI界面集成

基于YOLOv5/v7/v8的智能人臉表情識別系統:從算法原理到應用實現 表情識別的技術價值與挑戰 人臉表情識別(Facial Expression Recognition, FERYOLOv5/v7/v8等深度學習算法構建高效的表情識別系統,并設計直觀的UI界面集成方案。無論你是深度學習初學者還是有經驗的開發者,…

初步了解多線程

系列文章目錄 目錄 系列文章目錄 前言 一、進程 二、線程 1. 線程解決資源開銷的方式 2. 線程和進程的聯系和區別 三、多線程編程 1. 直觀了解多線程 2. 線程的創建方式 1. 繼承 Thread 重寫 run() 方法 2. 實現 Runable 接口&#xff0c;重寫 run() 方法 3. 繼承 …

安卓Android低功耗藍牙BLE連接異常報錯133

安卓Android低功耗藍牙BLE連接異常報錯133 之前連接一直好好的,不知道為什么今天突然就連接不了藍牙了,報錯133,按照 找網上的說明總是說清除GATT緩存,其實并不是我的問題,最后看到這里https://softs.im/android-ble-%e8%bf%9e%e6%8e%a5%e9%94%99%e8%af%af133/ 有如下說明: 情…

【分治】快排與歸并專題

分治思想 分&#xff08;Divide&#xff09;&#xff1a;將待排序數組不斷拆分為兩個等長&#xff08;或近似等長&#xff09;的子數組&#xff0c;直到子數組長度為 1&#xff08;天然有序&#xff09;。 治&#xff08;Conquer&#xff09;&#xff1a;遞歸排序每個子數組。 …

[Linux]學習筆記系列 -- mm/page_alloc

文章目錄mm/page_alloc.c 伙伴系統內存分配器(Buddy System Memory Allocator) 內核物理內存管理的核心歷史與背景這項技術是為了解決什么特定問題而誕生的&#xff1f;它的發展經歷了哪些重要的里程碑或版本迭代&#xff1f;目前該技術的社區活躍度和主流應用情況如何&#xf…

3秒傳輸大文件:cpolar+Localsend實現跨網絡秒傳

文章目錄前言1. 在Windows上安裝LocalSend2. 安裝Cpolar內網穿透3. 公網訪問LocalSend4. 固定LocalSend公網地址用 cpolar 讓 Localsend 突破距離限制就是這么簡單&#xff01;三步輕松搞定&#xff1a;在手機和電腦上都安裝 Localsend&#xff0c;在其中一臺設備上運行 cpolar…

基于STM32單片機智能RFID刷卡汽車位鎖樁設計

1 系統功能介紹 本系統是一個 基于 STM32 單片機的智能 RFID 刷卡車位鎖樁控制系統&#xff0c;其設計理念來源于現實中智能停車場的車位鎖樁管理。通過 RFID 刷卡認證、LCD1602 顯示、繼電器控制以及按鍵輔助操作&#xff0c;實現對車位的安全管理。該系統不僅模擬了車輛駛入與…

SQL185 試卷完成數同比2020年的增長率及排名變化

描述現有試卷信息表examination_info&#xff08;exam_id試卷ID, tag試卷類別, difficulty試卷難度, duration考試時長, release_time發布時間&#xff09;&#xff1a;試卷作答記錄表exam_record&#xff08;uid用戶ID, exam_id試卷ID, start_time開始作答時間, submit_time交…

網絡編程中的TCP——TCP的連接的建立、關閉、狀態轉移

網絡編程中的TCP——TCP的連接的建立、關閉、狀態轉移 TCP連接的建立和關閉wireshark捕獲數據&#xff1a;TCP三次握手四次揮手的時序圖&#xff1a;三次握手&#xff1a; 報文段1包含SYN標志&#xff0c;這是一個同步報文段&#xff0c;表示發起連接請求&#xff0c;包含自己起…

SQL 語句拼接在 C 語言中的實現與安全性分析

代碼解析 // 構建SQL插入語句 char *sql_insert (char *)malloc(sizeof(char) * 200); // 分配200字節內存 strcpy(sql_insert, "INSERT INTO user(username, passwd) VALUES("); // 復制基礎SQL語句 strcat(sql_insert, ""); // 添加單引號 strcat(sq…

`lock()` 和 `unlock()` 線程同步函數

1) 函數的概念與用途 lock() 和 unlock() 不是特定的標準庫函數&#xff0c;而是線程同步原語的一般概念&#xff0c;用于在多線程環境中保護共享資源。在不同的編程環境和庫中&#xff0c;這些函數有不同的具體實現&#xff08;如 POSIX 線程的 pthread_mutex_lock() 或 C 的 …

升級openssh后ORACLE RAC EM 安裝失敗處理

升級過程中由于SCP傳輸時目標目錄/tmp/tempRACTrans_2025_08_22--18-25-44-032/ractrans 不存在導致的OC4J配置失敗&#xff1a;WARNING: /usr/bin/scp: dest open "/tmp/tempRACTrans_2025_08_22--18-25-44-032/ractrans": No such file or directory/usr/bin/scp…

ADB 調試工具的學習[特殊字符]

一、ADB 的工作原理 1.1 ADB 概念 ADB (Android Debug Bridge)&#xff1a;Android 調試橋&#xff0c;是開發/測試 Android 應用必備的調試工具。作用&#xff1a;通過 電腦終端命令 操作 安卓手機/模擬器。 1.2 ADB 構成與原理 ADB 由三部分組成&#xff1a; Client 端&#…

用一根“數據中樞神經”串起業務從事件流到 Apache Kafka

1. 為什么是“事件流”&#xff1f; 在一個軟件定義、自動化、永遠在線的世界里&#xff0c;系統之間最需要的是&#xff1a;把發生了什么這件事&#xff0c;第一時間、按正確順序、可靠地傳到該知道的人/系統那里。 事件流就像企業的中樞神經&#xff1a;它把數據庫更新、設備…

【RAGFlow代碼詳解-4】數據存儲層

數據庫基礎設施 RAGFlow 使用關系數據庫&#xff08;MySQL 或 PostgreSQL&#xff09;作為主要元數據存儲&#xff0c;通過具有連接池和重試機制的 Peewee ORM 進行管理。 連接管理 數據庫連接通過 service_conf.yaml 和環境變量進行配置。該系統支持具有可配置連接池的 MySQL …

ES_映射

一、 映射&#xff08;Mapping&#xff09;是什么&#xff1f; 簡單來說&#xff0c;映射就像是關系型數據庫中的表結構定義&#xff08;Schema&#xff09;。它定義了索引&#xff08;Index&#xff09;中的文檔&#xff08;Document&#xff09;可以包含哪些字段&#xff08;…