Java中ArrayList集合的遍歷方式的多種方法
一、前言
Java中ArrayList是常用的數(shù)據(jù)結(jié)構(gòu)之一,它基于動(dòng)態(tài)數(shù)組實(shí)現(xiàn),允許我們存儲(chǔ)和操作對(duì)象集合。對(duì)ArrayList進(jìn)行遍歷是日常開(kāi)發(fā)中頻繁使用的操作,但遍歷方式多種多樣,不同場(chǎng)景下選擇合適的遍歷方式至關(guān)重要。本文我將詳細(xì)介紹ArrayList的各種遍歷方式、性能對(duì)比及適用場(chǎng)景,幫你在實(shí)際項(xiàng)目中做出最優(yōu)選擇。
二、ArrayList概述
ArrayList是Java集合框架中List接口的一個(gè)實(shí)現(xiàn)類,位于java.util包下。它的底層是基于動(dòng)態(tài)數(shù)組實(shí)現(xiàn)的,這意味著它可以根據(jù)元素的數(shù)量自動(dòng)調(diào)整容量。與傳統(tǒng)數(shù)組相比,ArrayList的容量可以動(dòng)態(tài)增長(zhǎng),提供了更靈活的元素存儲(chǔ)方式。
下面是一個(gè)簡(jiǎn)單創(chuàng)建和使用ArrayList的示例:
import java.util.ArrayList; import java.util.List; public class ArrayListDemo { public static void main(String[] args) { // 創(chuàng)建ArrayList實(shí)例 List<String> list = new ArrayList<>(); // 添加元素 list.add("Java"); list.add("Python"); list.add("C++"); // 訪問(wèn)元素 System.out.println("第一個(gè)元素:" + list.get(0)); // 修改元素 list.set(1, "JavaScript"); // 刪除元素 list.remove(2); // 打印ArrayList System.out.println("ArrayList內(nèi)容:" + list); } }
ArrayList的特點(diǎn)包括:
- 允許存儲(chǔ)null元素
- 元素有序且可重復(fù)
- 動(dòng)態(tài)擴(kuò)容,無(wú)需手動(dòng)管理容量
- 支持隨機(jī)訪問(wèn),通過(guò)索引快速訪問(wèn)元素
三、ArrayList的遍歷方式
1. 普通for循環(huán)遍歷
普通for循環(huán)是最基本的遍歷方式,通過(guò)控制索引來(lái)訪問(wèn)ArrayList中的每個(gè)元素。
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循環(huán)遍歷 for (int i = 0; i < list.size(); i++) { System.out.println("元素" + i + ":" + list.get(i)); } } }
優(yōu)點(diǎn):
- 可以精確控制遍歷的起始和結(jié)束位置
- 支持雙向遍歷(修改索引的遞增方式)
- 可以在遍歷過(guò)程中修改元素(通過(guò)set方法)
缺點(diǎn):
- 代碼相對(duì)繁瑣,需要手動(dòng)管理索引
- 如果不注意索引范圍,容易出現(xiàn)IndexOutOfBoundsException異常
2. 增強(qiáng)for循環(huán)遍歷
增強(qiáng)for循環(huán)(也稱為foreach循環(huán))是Java 5引入的語(yǔ)法糖,用于簡(jiǎn)化集合和數(shù)組的遍歷。
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"); // 增強(qiáng)for循環(huán)遍歷 for (String fruit : list) { System.out.println("水果:" + fruit); } } }
優(yōu)點(diǎn):
- 代碼簡(jiǎn)潔,減少了樣板代碼
- 提高了代碼的可讀性
- 避免了索引越界的風(fēng)險(xiǎn)
缺點(diǎn):
- 無(wú)法獲取當(dāng)前元素的索引(除非使用額外的計(jì)數(shù)器)
- 不支持在遍歷過(guò)程中修改集合結(jié)構(gòu)(如添加、刪除元素)
- 只能單向遍歷
3. 迭代器遍歷
迭代器(Iterator)是Java集合框架提供的一種標(biāo)準(zhǔn)遍歷機(jī)制,所有實(shí)現(xiàn)了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); // 可以安全地刪除當(dāng)前元素 if ("Banana".equals(fruit)) { iterator.remove(); } } System.out.println("刪除后的列表:" + list); } }
優(yōu)點(diǎn):
- 提供了統(tǒng)一的遍歷接口,適用于所有實(shí)現(xiàn)了Collection接口的集合
- 支持在遍歷過(guò)程中安全地刪除元素(通過(guò)Iterator的remove方法)
缺點(diǎn):
- 代碼相對(duì)冗長(zhǎng)
- 只能單向遍歷
- 性能略低于普通for循環(huán)
4. ListIterator遍歷
ListIterator是Iterator的子接口,專門(mén)用于遍歷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(從列表開(kāi)頭開(kāi)始) 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()); } // 在遍歷過(guò)程中添加元素 listIterator = list.listIterator(); while (listIterator.hasNext()) { String fruit = listIterator.next(); if ("Banana".equals(fruit)) { listIterator.add("Orange"); } } System.out.println("\n添加后的列表:" + list); } }
優(yōu)點(diǎn):
- 支持雙向遍歷(向前和向后)
- 支持在遍歷過(guò)程中添加、修改和刪除元素
- 可以獲取當(dāng)前元素的索引位置
缺點(diǎn):
- 只能用于List集合,通用性不如Iterator
- 代碼相對(duì)復(fù)雜,使用場(chǎng)景相對(duì)有限
5. Java 8 Stream API遍歷
Java 8引入的Stream API提供了一種函數(shù)式編程風(fēng)格的集合處理方式,可以更簡(jiǎn)潔地實(shí)現(xiàn)集合的遍歷和處理。
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); }); // 過(guò)濾并遍歷(只打印長(zhǎng)度大于5的水果) System.out.println("\n過(guò)濾后的結(jié)果:"); list.stream() .filter(fruit -> fruit.length() > 5) .forEach(fruit -> System.out.println("水果:" + fruit)); // 并行流遍歷(適用于大數(shù)據(jù)量并行處理) System.out.println("\n使用并行流遍歷:"); list.parallelStream().forEach(fruit -> { System.out.println("水果:" + fruit + "(線程:" + Thread.currentThread().getName() + ")"); }); } }
優(yōu)點(diǎn):
- 代碼簡(jiǎn)潔,支持鏈?zhǔn)讲僮?/li>
- 支持函數(shù)式編程風(fēng)格,提高代碼可讀性
- 可以方便地進(jìn)行過(guò)濾、映射、聚合等操作
- 并行流支持多線程并行處理,提高大數(shù)據(jù)量下的處理效率
缺點(diǎn):
- 對(duì)于簡(jiǎn)單的遍歷場(chǎng)景,可能顯得過(guò)于重量級(jí)
- 并行流在某些場(chǎng)景下可能引入線程安全問(wèn)題和額外的開(kāi)銷
- 調(diào)試相對(duì)困難
四、性能對(duì)比與分析
不同的遍歷方式在性能上可能存在差異,特別是在處理大量數(shù)據(jù)時(shí)。下面通過(guò)一個(gè)簡(jiǎn)單的性能測(cè)試來(lái)比較各種遍歷方式的執(zhí)行時(shí)間。
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) { // 創(chuàng)建一個(gè)包含100萬(wàn)個(gè)元素的ArrayList List<Integer> list = new ArrayList<>(); for (int i = 0; i < 1000000; i++) { list.add(i); } // 測(cè)試普通for循環(huán)遍歷 long startTime = System.currentTimeMillis(); for (int i = 0; i < list.size(); i++) { list.get(i); } long endTime = System.currentTimeMillis(); System.out.println("普通for循環(huán)遍歷耗時(shí):" + (endTime - startTime) + "ms"); // 測(cè)試增強(qiáng)for循環(huán)遍歷 startTime = System.currentTimeMillis(); for (Integer num : list) { // 空循環(huán),僅測(cè)試遍歷時(shí)間 } endTime = System.currentTimeMillis(); System.out.println("增強(qiáng)for循環(huán)遍歷耗時(shí):" + (endTime - startTime) + "ms"); // 測(cè)試迭代器遍歷 startTime = System.currentTimeMillis(); Iterator<Integer> iterator = list.iterator(); while (iterator.hasNext()) { iterator.next(); } endTime = System.currentTimeMillis(); System.out.println("迭代器遍歷耗時(shí):" + (endTime - startTime) + "ms"); // 測(cè)試ListIterator遍歷 startTime = System.currentTimeMillis(); ListIterator<Integer> listIterator = list.listIterator(); while (listIterator.hasNext()) { listIterator.next(); } endTime = System.currentTimeMillis(); System.out.println("ListIterator遍歷耗時(shí):" + (endTime - startTime) + "ms"); // 測(cè)試Stream API遍歷 startTime = System.currentTimeMillis(); list.stream().forEach(num -> { // 空處理,僅測(cè)試遍歷時(shí)間 }); endTime = System.currentTimeMillis(); System.out.println("Stream API遍歷耗時(shí):" + (endTime - startTime) + "ms"); // 測(cè)試并行流遍歷 startTime = System.currentTimeMillis(); list.parallelStream().forEach(num -> { // 空處理,僅測(cè)試遍歷時(shí)間 }); endTime = System.currentTimeMillis(); System.out.println("并行流遍歷耗時(shí):" + (endTime - startTime) + "ms"); } }
性能測(cè)試結(jié)果分析
在不同的測(cè)試環(huán)境下,各種遍歷方式的性能表現(xiàn)可能有所不同,但通??梢缘贸鲆韵陆Y(jié)論:
- 普通for循環(huán)在處理ArrayList時(shí)性能最優(yōu),因?yàn)樗苯油ㄟ^(guò)索引訪問(wèn)元素,沒(méi)有額外的開(kāi)銷。
- 增強(qiáng)for循環(huán)和迭代器遍歷的性能接近,它們?cè)诘讓訉?shí)現(xiàn)上是相似的。增強(qiáng)for循環(huán)實(shí)際上是迭代器的語(yǔ)法糖。
- ListIterator遍歷的性能略低于普通迭代器,因?yàn)樗峁┝烁嗟墓δ堋?/li>
- Stream API遍歷的性能在處理小數(shù)據(jù)量時(shí)與增強(qiáng)for循環(huán)接近,但在大數(shù)據(jù)量下可能略慢。
- 并行流遍歷在多核處理器上處理大數(shù)據(jù)量時(shí)性能優(yōu)勢(shì)明顯,但在小數(shù)據(jù)量或單核處理器上可能表現(xiàn)更差,因?yàn)椴⑿辛餍枰獎(jiǎng)?chuàng)建和管理線程池,帶來(lái)額外的開(kāi)銷。
五、遍歷方式的選擇建議
根據(jù)不同的場(chǎng)景和需求,可以選擇合適的遍歷方式:
- 如果需要在遍歷過(guò)程中修改集合結(jié)構(gòu)(添加、刪除元素),可以使用迭代器(Iterator或ListIterator)。特別是ListIterator支持在遍歷過(guò)程中添加、修改和刪除元素。
- 如果需要雙向遍歷,只能使用ListIterator。
- 如果代碼簡(jiǎn)潔性是首要考慮,增強(qiáng)for循環(huán)是不錯(cuò)的選擇。它適用于簡(jiǎn)單的遍歷場(chǎng)景,不需要索引和修改集合結(jié)構(gòu)的情況。
- 如果需要對(duì)元素進(jìn)行復(fù)雜的處理或聚合操作,Stream API提供了更強(qiáng)大和靈活的功能。它支持過(guò)濾、映射、排序、聚合等操作,可以使代碼更加簡(jiǎn)潔和可讀。
- 如果處理的數(shù)據(jù)量很大且在多核處理器上運(yùn)行,可以考慮使用并行流提高性能。但要注意并行流可能引入的線程安全問(wèn)題。
- 如果追求極致性能,特別是在處理大量數(shù)據(jù)時(shí),普通for循環(huán)是最佳選擇。
六、常見(jiàn)遍歷陷阱與注意事項(xiàng)
1. 并發(fā)修改異常(ConcurrentModificationException)
在使用增強(qiáng)for循環(huán)或迭代器遍歷集合時(shí),如果在遍歷過(guò)程中修改集合結(jié)構(gòu)(添加、刪除元素),會(huì)拋出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"); // 錯(cuò)誤示例:使用增強(qiáng)for循環(huán)刪除元素 try { for (String fruit : list) { if ("Banana".equals(fruit)) { list.remove(fruit); // 會(huì)拋出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. 迭代器失效問(wèn)題
在使用迭代器遍歷集合時(shí),如果通過(guò)集合本身的方法(而不是迭代器的方法)修改集合結(jié)構(gòu),會(huì)導(dǎo)致迭代器失效。
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)) { // 錯(cuò)誤:使用集合的remove方法,而不是迭代器的remove方法 list.remove(fruit); // 會(huì)導(dǎo)致迭代器失效 } } } }
3. 并行流的線程安全問(wèn)題
在使用并行流處理集合時(shí),如果操作共享資源,需要注意線程安全問(wèn)題。
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; // 錯(cuò)誤示例:并行流中修改非線程安全的共享變量 list.parallelStream().forEach(num -> { sum += num; // 線程不安全的操作 }); System.out.println("錯(cuò)誤的累加結(jié)果:" + sum); // 結(jié)果可能不正確 // 正確示例:使用線程安全的累加器 java.util.concurrent.atomic.AtomicInteger safeSum = new java.util.concurrent.atomic.AtomicInteger(0); list.parallelStream().forEach(num -> { safeSum.addAndGet(num); // 線程安全的操作 }); System.out.println("正確的累加結(jié)果:" + safeSum.get()); } }
總結(jié)
本文我詳細(xì)介紹了Java中ArrayList集合的多種遍歷方式:普通for循環(huán)、增強(qiáng)for循環(huán)、迭代器、ListIterator、Stream API和并行流。每種遍歷方式都有其特點(diǎn)和適用場(chǎng)景,應(yīng)根據(jù)具體需求選擇合適的遍歷方式。
在實(shí)際開(kāi)發(fā)中,除了考慮功能需求外,還應(yīng)關(guān)注代碼的性能和可讀性。對(duì)于簡(jiǎn)單的遍歷場(chǎng)景,建議優(yōu)先使用增強(qiáng)for循環(huán)或Stream API;對(duì)于需要高性能的大數(shù)據(jù)量處理,普通for循環(huán)或并行流是更好的選擇;而在需要靈活控制遍歷過(guò)程的場(chǎng)景下,迭代器或ListIterator則更為合適。
到此這篇關(guān)于Java中ArrayList集合的遍歷方式的多種方法的文章就介紹到這了,更多相關(guān)Java ArrayList集合遍歷內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java開(kāi)發(fā)Dubbo注解Adaptive實(shí)現(xiàn)原理
這篇文章主要為大家介紹了java開(kāi)發(fā)Dubbo注解Adaptive實(shí)現(xiàn)原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09lambda表達(dá)式解決java后臺(tái)分組排序過(guò)程解析
這篇文章主要介紹了lambda表達(dá)式解決java后臺(tái)分組排序過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10詳細(xì)分析Java并發(fā)集合LinkedBlockingQueue的用法
這篇文章主要介紹了詳細(xì)分析Java并發(fā)集合LinkedBlockingQueue的用法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04java中Iterator和ListIterator實(shí)例詳解
這篇文章主要介紹了java中Iterator和ListIterator實(shí)例詳解,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12SpringBoot+MinIO實(shí)現(xiàn)對(duì)象存儲(chǔ)方式
這篇文章主要介紹了SpringBoot+MinIO實(shí)現(xiàn)對(duì)象存儲(chǔ)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08JavaWeb實(shí)戰(zhàn)之用Servlet+JDBC實(shí)現(xiàn)用戶登錄與注冊(cè)
這篇文章主要介紹了JavaWeb實(shí)戰(zhàn)之用Servlet+JDBC實(shí)現(xiàn)用戶登錄與注冊(cè),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有很大的幫助,需要的朋友可以參考下2021-04-04每日六道java新手入門(mén)面試題,通往自由的道路第二天
這篇文章主要為大家分享了最有價(jià)值的6道java面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,對(duì)hashCode方法的設(shè)計(jì)、垃圾收集的堆和代進(jìn)行剖析,感興趣的小伙伴們可以參考一下2021-06-06基于mybatis查詢結(jié)果映射不到對(duì)象的處理
這篇文章主要介紹了mybatis查詢結(jié)果映射不到對(duì)象的處理方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08