Java-ArrayList集合的遍歷方式詳解

Java-ArrayList集合的遍歷方式詳解

    • 二、ArrayList概述
    • 三、ArrayList的遍歷方式
      • 1. 普通for循環遍歷
      • 2. 增強for循環遍歷
      • 3. 迭代器遍歷
      • 4. ListIterator遍歷
      • 5. Java 8 Stream API遍歷
    • 四、性能對比與分析
      • 性能測試結果分析
    • 五、遍歷方式的選擇建議
    • 六、常見遍歷陷阱與注意事項
      • 1. 并發修改異常(ConcurrentModificationException)
      • 2. 迭代器失效問題
      • 3. 并行流的線程安全問題
    • 總結

Java中ArrayList是常用的數據結構之一,它基于動態數組實現,允許我們存儲和操作對象集合。對ArrayList進行遍歷是日常開發中頻繁使用的操作,但遍歷方式多種多樣,不同場景下選擇合適的遍歷方式至關重要。本文我將詳細介紹ArrayList的各種遍歷方式、性能對比及適用場景,幫你在實際項目中做出最優選擇。

二、ArrayList概述

ArrayList是Java集合框架中List接口的一個實現類,位于java.util包下。它的底層是基于動態數組實現的,這意味著它可以根據元素的數量自動調整容量。與傳統數組相比,ArrayList的容量可以動態增長,提供了更靈活的元素存儲方式。

下面是一個簡單創建和使用ArrayList的示例:

import java.util.ArrayList;
import java.util.List;public class ArrayListDemo {public static void main(String[] args) {// 創建ArrayList實例List<String> list = new ArrayList<>();// 添加元素list.add("Java");list.add("Python");list.add("C++");// 訪問元素System.out.println("第一個元素:" + list.get(0));// 修改元素list.set(1, "JavaScript");// 刪除元素list.remove(2);// 打印ArrayListSystem.out.println("ArrayList內容:" + list);}
}

ArrayList的特點包括:

  • 允許存儲null元素
  • 元素有序且可重復
  • 動態擴容,無需手動管理容量
  • 支持隨機訪問,通過索引快速訪問元素

三、ArrayList的遍歷方式

1. 普通for循環遍歷

普通for循環是最基本的遍歷方式,通過控制索引來訪問ArrayList中的每個元素。

import java.util.ArrayList;
import java.util.List;public class ForLoopTraversal {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");list.add("Cherry");// 普通for循環遍歷for (int i = 0; i < list.size(); i++) {System.out.println("元素" + i + ":" + list.get(i));}}
}

優點

  • 可以精確控制遍歷的起始和結束位置
  • 支持雙向遍歷(修改索引的遞增方式)
  • 可以在遍歷過程中修改元素(通過set方法)

缺點

  • 代碼相對繁瑣,需要手動管理索引
  • 如果不注意索引范圍,容易出現IndexOutOfBoundsException異常

2. 增強for循環遍歷

增強for循環(也稱為foreach循環)是Java 5引入的語法糖,用于簡化集合和數組的遍歷。

import java.util.ArrayList;
import java.util.List;public class EnhancedForLoopTraversal {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");list.add("Cherry");// 增強for循環遍歷for (String fruit : list) {System.out.println("水果:" + fruit);}}
}

優點

  • 代碼簡潔,減少了樣板代碼
  • 提高了代碼的可讀性
  • 避免了索引越界的風險

缺點

  • 無法獲取當前元素的索引(除非使用額外的計數器)
  • 不支持在遍歷過程中修改集合結構(如添加、刪除元素)
  • 只能單向遍歷

3. 迭代器遍歷

迭代器(Iterator)是Java集合框架提供的一種標準遍歷機制,所有實現了Collection接口的集合類都支持迭代器。

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class IteratorTraversal {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");list.add("Cherry");// 獲取迭代器Iterator<String> iterator = list.iterator();// 使用迭代器遍歷while (iterator.hasNext()) {String fruit = iterator.next();System.out.println("水果:" + fruit);// 可以安全地刪除當前元素if ("Banana".equals(fruit)) {iterator.remove();}}System.out.println("刪除后的列表:" + list);}
}

優點

  • 提供了統一的遍歷接口,適用于所有實現了Collection接口的集合
  • 支持在遍歷過程中安全地刪除元素(通過Iterator的remove方法)

缺點

  • 代碼相對冗長
  • 只能單向遍歷
  • 性能略低于普通for循環

4. ListIterator遍歷

ListIterator是Iterator的子接口,專門用于遍歷List集合,提供了比Iterator更豐富的功能。

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;public class ListIteratorTraversal {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");list.add("Cherry");// 獲取ListIterator(從列表開頭開始)ListIterator<String> listIterator = list.listIterator();// 正向遍歷System.out.println("正向遍歷:");while (listIterator.hasNext()) {System.out.println("水果:" + listIterator.next());}// 反向遍歷(需要先將指針移到末尾)System.out.println("\n反向遍歷:");while (listIterator.hasPrevious()) {System.out.println("水果:" + listIterator.previous());}// 在遍歷過程中添加元素listIterator = list.listIterator();while (listIterator.hasNext()) {String fruit = listIterator.next();if ("Banana".equals(fruit)) {listIterator.add("Orange");}}System.out.println("\n添加后的列表:" + list);}
}

優點

  • 支持雙向遍歷(向前和向后)
  • 支持在遍歷過程中添加、修改和刪除元素
  • 可以獲取當前元素的索引位置

缺點

  • 只能用于List集合,通用性不如Iterator
  • 代碼相對復雜,使用場景相對有限

5. Java 8 Stream API遍歷

Java 8引入的Stream API提供了一種函數式編程風格的集合處理方式,可以更簡潔地實現集合的遍歷和處理。

import java.util.ArrayList;
import java.util.List;public class StreamApiTraversal {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");list.add("Cherry");// 使用Stream API的forEach方法遍歷System.out.println("使用Stream API遍歷:");list.stream().forEach(fruit -> {System.out.println("水果:" + fruit);});// 過濾并遍歷(只打印長度大于5的水果)System.out.println("\n過濾后的結果:");list.stream().filter(fruit -> fruit.length() > 5).forEach(fruit -> System.out.println("水果:" + fruit));// 并行流遍歷(適用于大數據量并行處理)System.out.println("\n使用并行流遍歷:");list.parallelStream().forEach(fruit -> {System.out.println("水果:" + fruit + "(線程:" + Thread.currentThread().getName() + ")");});}
}

優點

  • 代碼簡潔,支持鏈式操作
  • 支持函數式編程風格,提高代碼可讀性
  • 可以方便地進行過濾、映射、聚合等操作
  • 并行流支持多線程并行處理,提高大數據量下的處理效率

缺點

  • 對于簡單的遍歷場景,可能顯得過于重量級
  • 并行流在某些場景下可能引入線程安全問題和額外的開銷
  • 調試相對困難

四、性能對比與分析

不同的遍歷方式在性能上可能存在差異,特別是在處理大量數據時。下面通過一個簡單的性能測試來比較各種遍歷方式的執行時間。

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;public class PerformanceComparison {public static void main(String[] args) {// 創建一個包含100萬個元素的ArrayListList<Integer> list = new ArrayList<>();for (int i = 0; i < 1000000; i++) {list.add(i);}// 測試普通for循環遍歷long startTime = System.currentTimeMillis();for (int i = 0; i < list.size(); i++) {list.get(i);}long endTime = System.currentTimeMillis();System.out.println("普通for循環遍歷耗時:" + (endTime - startTime) + "ms");// 測試增強for循環遍歷startTime = System.currentTimeMillis();for (Integer num : list) {// 空循環,僅測試遍歷時間}endTime = System.currentTimeMillis();System.out.println("增強for循環遍歷耗時:" + (endTime - startTime) + "ms");// 測試迭代器遍歷startTime = System.currentTimeMillis();Iterator<Integer> iterator = list.iterator();while (iterator.hasNext()) {iterator.next();}endTime = System.currentTimeMillis();System.out.println("迭代器遍歷耗時:" + (endTime - startTime) + "ms");// 測試ListIterator遍歷startTime = System.currentTimeMillis();ListIterator<Integer> listIterator = list.listIterator();while (listIterator.hasNext()) {listIterator.next();}endTime = System.currentTimeMillis();System.out.println("ListIterator遍歷耗時:" + (endTime - startTime) + "ms");// 測試Stream API遍歷startTime = System.currentTimeMillis();list.stream().forEach(num -> {// 空處理,僅測試遍歷時間});endTime = System.currentTimeMillis();System.out.println("Stream API遍歷耗時:" + (endTime - startTime) + "ms");// 測試并行流遍歷startTime = System.currentTimeMillis();list.parallelStream().forEach(num -> {// 空處理,僅測試遍歷時間});endTime = System.currentTimeMillis();System.out.println("并行流遍歷耗時:" + (endTime - startTime) + "ms");}
}

性能測試結果分析

在不同的測試環境下,各種遍歷方式的性能表現可能有所不同,但通常可以得出以下結論:

  1. 普通for循環在處理ArrayList時性能最優,因為它直接通過索引訪問元素,沒有額外的開銷。

  2. 增強for循環迭代器遍歷的性能接近,它們在底層實現上是相似的。增強for循環實際上是迭代器的語法糖。

  3. ListIterator遍歷的性能略低于普通迭代器,因為它提供了更多的功能。

  4. Stream API遍歷的性能在處理小數據量時與增強for循環接近,但在大數據量下可能略慢。

  5. 并行流遍歷在多核處理器上處理大數據量時性能優勢明顯,但在小數據量或單核處理器上可能表現更差,因為并行流需要創建和管理線程池,帶來額外的開銷。

五、遍歷方式的選擇建議

根據不同的場景和需求,可以選擇合適的遍歷方式:

  1. 如果需要在遍歷過程中修改集合結構(添加、刪除元素),可以使用迭代器(Iterator或ListIterator)。特別是ListIterator支持在遍歷過程中添加、修改和刪除元素。

  2. 如果需要雙向遍歷,只能使用ListIterator。

  3. 如果代碼簡潔性是首要考慮,增強for循環是不錯的選擇。它適用于簡單的遍歷場景,不需要索引和修改集合結構的情況。

  4. 如果需要對元素進行復雜的處理或聚合操作,Stream API提供了更強大和靈活的功能。它支持過濾、映射、排序、聚合等操作,可以使代碼更加簡潔和可讀。

  5. 如果處理的數據量很大且在多核處理器上運行,可以考慮使用并行流提高性能。但要注意并行流可能引入的線程安全問題。

  6. 如果追求極致性能,特別是在處理大量數據時,普通for循環是最佳選擇。

六、常見遍歷陷阱與注意事項

1. 并發修改異常(ConcurrentModificationException)

在使用增強for循環或迭代器遍歷集合時,如果在遍歷過程中修改集合結構(添加、刪除元素),會拋出ConcurrentModificationException異常。

import java.util.ArrayList;
import java.util.List;public class ConcurrentModificationExample {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");list.add("Cherry");// 錯誤示例:使用增強for循環刪除元素try {for (String fruit : list) {if ("Banana".equals(fruit)) {list.remove(fruit); // 會拋出ConcurrentModificationException}}} catch (Exception e) {e.printStackTrace();}// 正確示例:使用迭代器刪除元素list = new ArrayList<>();list.add("Apple");list.add("Banana");list.add("Cherry");java.util.Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {String fruit = iterator.next();if ("Banana".equals(fruit)) {iterator.remove(); // 安全刪除元素}}System.out.println("刪除后的列表:" + list);}
}

2. 迭代器失效問題

在使用迭代器遍歷集合時,如果通過集合本身的方法(而不是迭代器的方法)修改集合結構,會導致迭代器失效。

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class IteratorInvalidationExample {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("Apple");list.add("Banana");list.add("Cherry");Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {String fruit = iterator.next();if ("Banana".equals(fruit)) {// 錯誤:使用集合的remove方法,而不是迭代器的remove方法list.remove(fruit); // 會導致迭代器失效}}}
}

3. 并行流的線程安全問題

在使用并行流處理集合時,如果操作共享資源,需要注意線程安全問題。

import java.util.ArrayList;
import java.util.List;public class ParallelStreamThreadSafety {public static void main(String[] args) {List<Integer> list = new ArrayList<>();for (int i = 0; i < 1000; i++) {list.add(i);}// 非線程安全的累加器int sum = 0;// 錯誤示例:并行流中修改非線程安全的共享變量list.parallelStream().forEach(num -> {sum += num; // 線程不安全的操作});System.out.println("錯誤的累加結果:" + sum); // 結果可能不正確// 正確示例:使用線程安全的累加器java.util.concurrent.atomic.AtomicInteger safeSum = new java.util.concurrent.atomic.AtomicInteger(0);list.parallelStream().forEach(num -> {safeSum.addAndGet(num); // 線程安全的操作});System.out.println("正確的累加結果:" + safeSum.get());}
}

總結

本文我詳細介紹了Java中ArrayList集合的多種遍歷方式:普通for循環、增強for循環、迭代器、ListIterator、Stream API和并行流。每種遍歷方式都有其特點和適用場景,應根據具體需求選擇合適的遍歷方式。

在實際開發中,除了考慮功能需求外,還應關注代碼的性能和可讀性。對于簡單的遍歷場景,建議優先使用增強for循環或Stream API;對于需要高性能的大數據量處理,普通for循環或并行流是更好的選擇;而在需要靈活控制遍歷過程的場景下,迭代器或ListIterator則更為合適。

若這篇內容幫到你,動動手指支持下!關注不迷路,干貨持續輸出!
ヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノヾ(′? ˋ)ノ

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

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

相關文章

華為網路設備學習-23(路由器OSPF-LSA及特殊詳解 二)

OSPF動態路由協議要求&#xff1a; 1.必須有一個骨干區域&#xff08;Area 0&#xff09;。有且僅有一個&#xff0c;而且連續不可分割。 2.所有非骨干區域&#xff08;Area 1-n&#xff09;必須和骨干區域&#xff08;Area 0&#xff09;直接相連&#xff0c;且所有區域之間…

基于大模型的急性腐蝕性胃炎風險預測與診療方案研究報告

目錄 一、引言 1.1 研究背景與意義 1.2 研究目的 1.3 國內外研究現狀 二、急性腐蝕性胃炎概述 2.1 定義與發病機制 2.2 病因分析 2.3 臨床表現與分型 2.4 診斷方法 三、大模型技術介紹 3.1 大模型原理 3.2 常用大模型及在醫療領域應用案例 3.3 選擇用于急性腐蝕性…

泰迪杯特等獎案例深度解析:基于三維點云與深度學習的復雜零件裝配質量檢測系統設計

一、案例背景與行業痛點 1.1 工業裝配質檢的現狀與挑戰 在精密制造領域(如航空航天發動機、新能源汽車電池模組),復雜零件的裝配質量直接影響產品性能與安全性。傳統人工質檢存在效率低(單件檢測耗時>3分鐘)、漏檢率高(約15%)等問題,而現有自動化方案面臨以下技術…

離散傅里葉變換DFT推導及理解

DTFT到DFT的推導 關于DTFT的相關推導已經做過總結&#xff0c;詳見《DTFT及其反變換的直觀理解》&#xff0c;每一個離散的頻率分量都是由時域中的復指數信號累加得到的&#xff0c;DTFT得到的頻譜時頻率的連續函數 。 離散時間傅里葉變換公式&#xff0c;式1&#xff1a; 將…

欣佰特科技|工業 / 農業 / AR 場景怎么選?Stereolabs ZED 雙目3D相機型號對比與選型建議

Stereolabs ZED 相機系列為視覺感知領域提供了多種創新解決方案&#xff0c;適用于不同應用場景。選擇合適的 ZED 相機型號&#xff0c;需綜合考慮分辨率、深度感知范圍、接口類型等因素。 Stereolabs ZED 相機產品系列概覽 ZED&#xff1a;首款立體視覺相機&#xff0c;專為高…

黑馬點評Reids重點詳解(Reids使用重點)

目錄 一、短信登錄&#xff08;redisseesion&#xff09; 基于Session實現登錄流程 &#x1f504; 圖中關鍵模塊解釋&#xff1a; 利用seesion登錄的問題 設計key的具體細節 整體訪問流程 二、商戶查詢緩存 reids與數據庫主動更新的三種方案 緩存穿透 緩存雪崩問題及…

【Pandas】pandas DataFrame add_suffix

Pandas2.2 DataFrame Reindexing selection label manipulation 方法描述DataFrame.add_prefix(prefix[, axis])用于在 DataFrame 的行標簽或列標簽前添加指定前綴的方法DataFrame.add_suffix(suffix[, axis])用于在 DataFrame 的行標簽或列標簽后添加指定后綴的方法 pandas…

解鎖MCP:AI大模型的萬能工具箱

摘要&#xff1a;MCP&#xff08;Model Context Protocol&#xff0c;模型上下文協議&#xff09;是由Anthropic開源發布的一項技術&#xff0c;旨在作為AI大模型與外部數據和工具之間溝通的“通用語言”。它通過標準化協議&#xff0c;讓大模型能夠自動調用外部工具完成任務&a…

nginx性能調優與深度監控

目錄 nginx性能調優 更改進程數與連接數 進程數 連接數 靜態緩存功能設置 日志切割 配置網頁壓縮 nginx 的深度監控 GoAccess 簡介 GoAccess安裝 ?編輯 配置中文環境 GOAccess生成中文報告 測試訪問 nginx vts 簡介 nginx vts 安裝 nginx配置開啟vts 測試訪問…

【時時三省】Python 語言----牛客網刷題筆記

目錄 1,常用函數 1,input() 2,map() 3,split() 4,range() 5, 切片 6,列表推導式 山不在高,有仙則名。水不在深,有龍則靈。 ----CSDN 時時三省 1,常用函數 1,input() 該函數遇到 換行停止接收,返回類型為字符串 2,map() 該函數出鏡率較高,目的是將一個可迭…

docker compose yml 啟動的容器中,如何使用linux環境變量賦值

在 Docker Compose 中&#xff0c;可以通過環境變量&#xff08;${VAR} 或 $VAR&#xff09;來動態配置容器。以下是幾種常見的使用方式 - 使用 env_file 加載變量文件 可以單獨定義一個環境變量文件&#xff08;如 app.env&#xff09;&#xff0c;然后在 docker-compose.y…

深入解析Kafka JVM堆內存:優化策略與監控實踐

&#x1f49d;&#x1f49d;&#x1f49d;歡迎蒞臨我的博客&#xff0c;很高興能夠在這里和您見面&#xff01;希望您在這里可以感受到一份輕松愉快的氛圍&#xff0c;不僅可以獲得有趣的內容和知識&#xff0c;也可以暢所欲言、分享您的想法和見解。 推薦&#xff1a;「storms…

git常用操作命令

本文介紹git常用的操作命令&#xff0c;供大家參考。 1、開始 # 初始化本地git git init# 在初始化的目錄中&#xff0c;創建readme.txt&#xff0c;添加到git庫中 git add readme.txt git commit -m "寫了一個readme.txt文件"2、版本回退 2.1、git reset git lo…

解鎖 MCP 中的 JSON-RPC:跨平臺通信的奧秘

你好,我是 shengjk1,多年大廠經驗,努力構建 通俗易懂的、好玩的編程語言教程。 歡迎關注!你會有如下收益: 了解大廠經驗擁有和大廠相匹配的技術等希望看什么,評論或者私信告訴我! 文章目錄 零、 背景一、RPC vs HTTP1.1 什么是RPC1.2 為什么需要 RPC?1.3 RPC 解決了什么…

【Redis】第1節|Redis服務搭建

一、Redis 基礎概念 核心功能 內存數據庫&#xff0c;支持持久化&#xff08;RDB/AOF&#xff09;、主從復制、哨兵高可用、集群分片。常用場景&#xff1a;緩存、分布式鎖、消息隊列、計數器、排行榜等。 安裝環境 依賴 GCC 環境&#xff08;C語言編譯&#xff09;&#xff0…

GitLab-CI簡介

概述 持續集成&#xff08;CI&#xff09;和 持續交付(CD) 是一種流行的軟件開發實踐&#xff0c;每次提交都通過自動化的構建&#xff08;測試、編譯、發布&#xff09;來驗證&#xff0c;從而盡早的發現錯誤。 持續集成實現了DevOps, 使開發人員和運維人員從繁瑣的工作中解…

FFmpeg解碼器配置指南:為什么--enable-decoders不能單獨使用?

FFmpeg解碼器配置指南 在FFmpeg的編譯配置過程中&#xff0c;許多開發者會遇到關于解碼器配置的困惑。特別是--enable-decoders這個選項&#xff0c;很多人誤以為啟用它就能自動包含所有解碼器。本文將深入解析FFmpeg解碼器配置的機制&#xff0c;并通過實際測試展示正確的配置…

C++多態與虛函數

C++多態與虛函數詳解 多態(Polymorphism)是 C++ 面向對象編程的重要特性,通過統一的接口實現不同的行為。虛函數(Virtual Function)是實現運行時多態的核心機制。以下從多態的構成條件、意義、析構函數的虛函數化、純虛函數和抽象類,以及虛函數表的底層實現依次介紹。 1.…

游戲引擎學習第313天:回到 Z 層級的工作

回顧并為今天的內容定下基調 昨天我們新增了每個元素級別的排序功能&#xff0c;并且采用了一種我們認為挺有意思的方法。原本計劃采用一個更復雜的實現方式&#xff0c;但在中途實現的過程中&#xff0c;突然意識到其實有個更簡單的做法&#xff0c;于是我們就改用了這個簡單…

ODBC簡介

ODBC&#xff08;Open Database Connectivity&#xff09;是一個由 Microsoft 制定的標準接口&#xff0c;允許不同的應用程序通過統一的方式訪問各種數據庫系統。 &#x1f9e0; 簡單理解&#xff1a; ODBC 就像是 “翻譯官”&#xff0c;在應用程序&#xff08;如 Excel、Py…