Java集合遍歷全指南(迭代器&增強(qiáng)for及列表迭代器)
文章摘要
- 閱讀時(shí)長:12 分鐘
- 適合人群:
- Java 集合初學(xué)者 → 重點(diǎn)看:迭代器、增強(qiáng) for、Lambda、普通 for、列表迭代器全套用法
- 易踩坑:遍歷中增刪元素的同學(xué) → 重點(diǎn)看:并發(fā)修改異常、迭代器 remove、列表迭代器 add
- 面試復(fù)習(xí) → 重點(diǎn)看:Iterator 底層原理、cursor 指針、expectedModCount、并發(fā)修改根源
- 寫技術(shù)筆記/總結(jié) → 重點(diǎn)看:結(jié)構(gòu)清晰、表格化、細(xì)節(jié)標(biāo)注、完整代碼
- 本文內(nèi)容:
全覆蓋 迭代器 Iterator、列表迭代器 ListIterator、增強(qiáng) for、遍歷方式匯總、底層源碼,保留所有核心知識點(diǎn),搭配代碼示例+避坑指南+原理拆解,邏輯連貫、干貨拉滿,助力快速掌握集合遍歷精髓。
迭代器與增強(qiáng) for 循環(huán)
一、迭代器遍歷(Iterator 接口)
迭代器是 不依賴索引 的通用遍歷方式,適配所有 Collection 系列集合(List、Set 均可用),是集合專屬的標(biāo)準(zhǔn)遍歷工具,無需關(guān)心集合底層存儲結(jié)構(gòu),只需專注“遍歷取元素”。
獲取迭代器
Collection 集合通過專屬方法獲取迭代器對象,默認(rèn)指針指向集合 0 索引位置:
| 方法名 | 說明 |
|---|---|
Iterator<E> iterator() | 返回迭代器對象,默認(rèn)指向當(dāng)前集合 0 索引 |
迭代器核心成員方法
迭代器的用法固定,核心就是“判斷有沒有 → 取元素 → (可選)刪元素”三步,常用方法如下:
| 方法名 | 說明 |
|---|---|
boolean hasNext() | 判斷當(dāng)前指針位置是否有元素,有則返回 true,無則返回 false(避免越界) |
E next() | 獲取當(dāng)前指針位置的元素,并將迭代器指針自動移向下一個位置 |
default void remove() | 刪除上一次 next() 方法返回的元素(僅迭代器遍歷期間可用) |
迭代器四大關(guān)鍵細(xì)節(jié)(必背避坑,少踩90%錯誤)
- 邊界異常:當(dāng) hasNext() 返回 false 時(shí),繼續(xù)調(diào)用 next(),會拋出
NoSuchElementException(無此元素異常),切記“先判斷、再取值”。 - 指針不復(fù)位:迭代器遍歷完畢后,指針會停留在集合末尾,不會自動回到 0 索引;如果想再次遍歷集合,必須重新調(diào)用 iterator() 方法,獲取一個新的迭代器對象。
- next() 調(diào)用規(guī)范:一次循環(huán)中建議只調(diào)用一次 next() 方法,保證 hasNext() 與 next() 一一對應(yīng),避免出現(xiàn)指針錯亂、元素漏取或重復(fù)取的問題。
- 并發(fā)修改異常:迭代器遍歷集合期間,不能使用集合自身的 add()、remove() 方法 操作元素(比如 list.add()、list.remove()),否則會拋出
ConcurrentModificationException;若確實(shí)需要刪除元素,只能使用迭代器自身的 remove() 方法;若需要添加元素,普通迭代器無法實(shí)現(xiàn)(需用列表迭代器)。
迭代器遍歷代碼示例(完整可運(yùn)行)
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
// 1. 獲取迭代器對象
Iterator<String> it = list.iterator();
// 2. 遍歷:先判斷,再取值
while (it.hasNext()) {
// 3. 獲取當(dāng)前元素,指針自動后移
String str = it.next();
System.out.println(str);
// 可選:刪除上一次next()返回的元素(此處演示刪除"bbb")
if ("bbb".equals(str)) {
it.remove(); // 僅迭代器remove可用,不會拋并發(fā)修改異常
}
}
// 遍歷完畢,指針不復(fù)位,重新遍歷需重新獲取迭代器
Iterator<String> it2 = list.iterator();
while (it2.hasNext()) {
System.out.println(it2.next()); // 輸出:aaa、ccc
}
}
}
二、列表迭代器遍歷(ListIterator 接口)
列表迭代器是 List 集合專屬 的遍歷工具,繼承自 Iterator 接口,完全實(shí)現(xiàn)了 Iterator 的所有功能(hasNext()、next()、remove()),同時(shí)新增了“向前遍歷、遍歷中添加元素”的功能,完美解決了普通迭代器不能添加元素的痛點(diǎn)。
列表迭代器常用成員方法(新增+繼承)
| 方法名 | 說明 |
|---|---|
boolean hasNext() | (繼承)判斷當(dāng)前位置的下一個位置是否有元素,有為 true,無為 false |
E next() | (繼承)獲取當(dāng)前位置元素,并將迭代器指針移向后一個位置 |
void remove() | (繼承)刪除上一次 next() 或 previous() 方法返回的元素 |
boolean hasPrevious() | (新增)判斷當(dāng)前位置的前一個位置是否有元素,有為 true,無為 false(支持向前遍歷) |
E previous() | (新增)獲取當(dāng)前位置前一個元素,并將迭代器指針向前移一個位置(向前遍歷核心) |
public void add(E e) | (新增)將元素插入當(dāng)前指針位置,并將迭代器對象移向后一個位置(遍歷中添加元素) |
列表迭代器核心優(yōu)勢
- 僅 List 集合可用(ArrayList、LinkedList 等),Set 集合不能使用。
- 支持“雙向遍歷”:既能向后遍歷(next()),也能向前遍歷(previous())。
- 遍歷期間可自由添加、刪除元素,不會拋出并發(fā)修改異常。
列表迭代器代碼示例(遍歷中添加元素)
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class ListIteratorDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
// 1. 獲取List集合專屬的列表迭代器
ListIterator<String> it2 = list.listIterator();
// 2. 向后遍歷,遍歷中添加元素
while (it2.hasNext()) {
String str2 = it2.next();
System.out.println(str2);
// 當(dāng)遍歷到"bbb"時(shí),在其后面添加"qqq"
if ("bbb".equals(str2)) {
it2.add("qqq"); // 遍歷中添加,無異常
}
}
System.out.println("添加元素后:" + list); // 輸出:[aaa, bbb, qqq, ccc]
// 3. 向前遍歷(演示雙向遍歷)
System.out.println("向前遍歷:");
while (it2.hasPrevious()) {
String str3 = it2.previous();
System.out.println(str3); // 輸出:ccc、qqq、bbb、aaa
}
}
}
三、增強(qiáng) for 遍歷
增強(qiáng) for 循環(huán)是 JDK 5 以后引入的語法糖,底層本質(zhì)就是一個 Iterator 迭代器,目的是簡化迭代器的代碼書寫,讓遍歷變得更簡潔、更易讀,無需手動獲取迭代器、判斷 hasNext()、調(diào)用 next()。
增強(qiáng) for 核心特點(diǎn)
- 語法極簡,僅用于“遍歷”,不能用于遍歷中添加、刪除元素(否則拋并發(fā)修改異常)。
- 適配范圍:所有單列集合(Collection 及其子類)和數(shù)組,雙列集合(Map)不能直接使用。
- 遍歷過程中,不能操作集合的索引(無索引相關(guān)方法)。
增強(qiáng) for 標(biāo)準(zhǔn)格式
for(元素的數(shù)據(jù)類型 變量名 : 數(shù)組或者單列集合) {
// 循環(huán)體:變量名 依次表示集合/數(shù)組中的每一個元素
}
增強(qiáng) for 代碼示例(集合+數(shù)組)
import java.util.ArrayList;
import java.util.List;
public class ForEachDemo {
public static void main(String[] args) {
// 1. 增強(qiáng) for 遍歷 List 集合
List<String> list = new ArrayList<>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
for (String s : list) {
System.out.println(s); // s 依次表示集合中的每一個元素
}
// 2. 增強(qiáng) for 遍歷數(shù)組
Integer[] arr = {1, 2, 3, 4, 5};
for (Integer num : arr) {
System.out.println(num); // num 依次表示數(shù)組中的每一個元素
}
// 重要細(xì)節(jié):修改第三方變量s,不會改變集合本身的值
for (String s : list) {
s = "zhaoliu"; // 僅修改變量s的值,集合中的元素不變
}
System.out.println("修改后集合:" + list); // 輸出:[zhangsan, lisi, wangwu]
}
}
實(shí)用小技巧
增強(qiáng) for 快速生成:在 IDEA 中,輸入“集合/數(shù)組名 + for”,按下回車鍵,即可自動生成增強(qiáng) for 循環(huán)代碼,高效又不易出錯。
遍歷方式大匯總(實(shí)用度拉滿)
結(jié)合前面所學(xué),我們整理出 Collection 和 List 集合的所有遍歷方式,明確每種方式的適用場景,避免盲目使用。
一、Collection 集合的三種遍歷方式(通用)
| 遍歷方式 | 核心適用場景 | 優(yōu)勢 | 不足 |
|---|---|---|---|
| 迭代器(Iterator) | 遍歷中需要刪除元素 | 通用、可刪除元素 | 語法相對繁瑣 |
| 增強(qiáng) for 循環(huán) | 僅需要遍歷元素,無需修改 | 語法極簡、易讀 | 不能增刪元素、無索引 |
| Lambda 表達(dá)式遍歷 | 僅需要遍歷元素,追求代碼簡潔 | 最簡潔、代碼量最少 | 不能增刪元素、無索引 |
簡單總結(jié):
- 遍歷中需要刪除元素 → 用 迭代器
- 僅僅想遍歷,追求簡潔 → 用 Lambda 或 增強(qiáng) for
二、List 集合的五種遍歷方式(最全)
List 集合因?yàn)橛兴饕?,除了繼承 Collection 的三種遍歷方式,還新增了“普通 for 循環(huán)”和“列表迭代器”兩種方式,共五種,覆蓋所有場景:
| 遍歷方式 | 核心適用場景 | 優(yōu)勢 |
|---|---|---|
| 迭代器(Iterator) | 遍歷中需要刪除元素 | 通用、可刪除 |
| 列表迭代器(ListIterator) | 遍歷中需要添加/刪除元素、雙向遍歷 | 可增刪、可雙向遍歷 |
| 增強(qiáng) for 循環(huán) | 僅遍歷,無需修改 | 簡潔易讀 |
| Lambda 表達(dá)式遍歷 | 僅遍歷,追求極簡 | 代碼量最少 |
| 普通 for 循環(huán) | 遍歷中需要操作索引(如修改指定位置元素) | 可操作索引、靈活 |
簡單總結(jié):
- 遍歷中增刪 → 列表迭代器
- 遍歷中刪 → 迭代器
- 操作索引 → 普通 for
- 僅遍歷 → Lambda / 增強(qiáng) for
全套遍歷方式代碼示例(一次性掌握)
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class AllTraversalDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
// 1. 迭代器遍歷(可刪除)
System.out.println("=== 迭代器遍歷 ===");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String str = it.next();
System.out.println(str);
}
// 2. 列表迭代器遍歷(可增刪、雙向)
System.out.println("=== 列表迭代器遍歷 ===");
ListIterator<String> it2 = list.listIterator();
while (it2.hasNext()) {
String str2 = it2.next();
if ("bbb".equals(str2)) {
it2.add("qqq"); // 遍歷中添加
}
System.out.println(str2);
}
// 3. 增強(qiáng) for 遍歷(僅遍歷)
System.out.println("=== 增強(qiáng) for 遍歷 ===");
for (String s : list) {
System.out.println(s);
}
// 4. 普通 for 循環(huán)遍歷(操作索引)
System.out.println("=== 普通 for 遍歷 ===");
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
// 可操作索引:修改指定位置元素
if (i == 0) {
list.set(i, "aaa111");
}
}
// 5. Lambda 表達(dá)式遍歷(極簡)
System.out.println("=== Lambda 遍歷 ===");
list.forEach(s -> System.out.println(s));
}
}
Iterator 底層源碼解析(面試高頻)
很多同學(xué)在遍歷集合時(shí)會遇到并發(fā)修改異常,卻不知道根源在哪里;面試中,面試官也常問“迭代器底層原理”,看懂這段源碼,就能徹底搞懂所有問題(以 ArrayList 中的迭代器為例):
// ArrayList 中獲取迭代器的方法
public Iterator<E> iterator() {
return new Itr(); // 每次調(diào)用iterator(),都會創(chuàng)建一個新的Itr對象(內(nèi)部類)
}
// 迭代器的底層實(shí)現(xiàn):ArrayList的內(nèi)部類Itr,實(shí)現(xiàn)了Iterator接口
private class Itr implements Iterator<E> {
int cursor; // 迭代器指針(光標(biāo)),默認(rèn)指向集合0索引
int lastRet = -1; // 記錄上一次調(diào)用next()返回的元素索引,初始值-1(表示未操作)
int expectedModCount = modCount; // 記錄集合的修改次數(shù)(創(chuàng)建迭代器時(shí)的初始次數(shù))
// hasNext()原理:判斷指針是否到達(dá)集合末尾(cursor等于集合大小,說明無元素)
public boolean hasNext() {
return cursor != size;
}
// next()原理:獲取當(dāng)前指針元素,指針后移,并校驗(yàn)集合是否被修改
public E next() {
checkForComodification(); // 核心校驗(yàn):判斷集合是否被非法修改
int i = cursor; // 記錄當(dāng)前指針位置
if (i >= size)
throw new NoSuchElementException(); // 指針越界,拋異常
Object[] elementData = ArrayList.this.elementData; // 獲取ArrayList底層數(shù)組
if (i >= elementData.length)
throw new ConcurrentModificationException(); // 并發(fā)修改異常
cursor = i + 1; // 指針后移一位
return (E) elementData[lastRet = i]; // 返回當(dāng)前元素,并更新lastRet
}
// 校驗(yàn)集合是否被修改:expectedModCount(迭代器記錄的次數(shù)) != modCount(集合實(shí)際次數(shù))
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
源碼核心解讀(一句話搞懂)
cursor:迭代器的指針,控制遍歷的位置,默認(rèn)0,每次next()后+1。modCount:ArrayList 中的成員變量,記錄集合的修改次數(shù)(每調(diào)用一次 add()、remove(),modCount 就自增1)。expectedModCount:迭代器創(chuàng)建時(shí),復(fù)制一份當(dāng)前集合的 modCount,作為“預(yù)期修改次數(shù)”。- 并發(fā)修改異常根源:遍歷期間,若用集合的 add()、remove() 方法修改元素,會導(dǎo)致
modCount自增,此時(shí)modCount != expectedModCount,調(diào)用 next() 時(shí),checkForComodification() 校驗(yàn)失敗,拋出異常。
避坑關(guān)鍵
遍歷期間,只要不修改 modCount 和 expectedModCount 的差值,就不會拋異常:
- 用迭代器的 remove():會同步更新
expectedModCount,保證與 modCount 一致。 - 用集合的 add()/remove():僅修改 modCount,不更新 expectedModCount,觸發(fā)異常。
全篇核心干貨總結(jié)(速記版)
- 迭代器(Iterator):通用遍歷,可刪不可加,指針不復(fù)位,遍歷中不能用集合方法修改元素。
- 列表迭代器(ListIterator):List 專屬,可增可刪、雙向遍歷,解決普通迭代器不能添加的痛點(diǎn)。
- 增強(qiáng) for:底層是迭代器,語法極簡,僅用于遍歷,不能增刪元素,修改第三方變量不影響集合。
- 遍歷方式選擇:
- Collection:迭代器(刪)、增強(qiáng) for/Lambda(僅遍歷)
- List:新增普通 for(操作索引)、列表迭代器(增刪)
- 并發(fā)修改異常:根源是 modCount != expectedModCount,遍歷中用迭代器修改元素可避免。
- 面試重點(diǎn):Iterator 底層源碼(cursor、modCount、expectedModCount)、列表迭代器新增功能。
寫在最后
集合遍歷是 Java 基礎(chǔ)中最常用、最易出錯的知識點(diǎn),無論是日常業(yè)務(wù)開發(fā)、刷算法題,還是面試,都會高頻遇到。
很多初學(xué)者容易在“遍歷中增刪元素”“指針復(fù)位”“并發(fā)修改異常”這幾個點(diǎn)上踩坑,其實(shí)只要吃透本文的知識點(diǎn),多敲幾遍代碼,理解底層原理,就能輕松規(guī)避所有問題。
建議大家把文中的代碼親手敲一遍,重點(diǎn)練習(xí)“迭代器刪除”“列表迭代器添加”“并發(fā)修改異常規(guī)避”這三個場景,加深記憶。
以上就是Java集合遍歷全指南(迭代器&增強(qiáng)for及列表迭代器)的詳細(xì)內(nèi)容,更多關(guān)于Java集合遍歷指南的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring?Boot?3.3?實(shí)現(xiàn)職責(zé)鏈模式輕松應(yīng)對電商訂單流程分析
在電商系統(tǒng)中,訂單處理流程包括庫存校驗(yàn)、優(yōu)惠券驗(yàn)證、運(yùn)費(fèi)計(jì)算等多個步驟,這些步驟具有順序依賴性,為了管理這些業(yè)務(wù)邏輯,職責(zé)鏈模式提供了解決方案,通過鏈?zhǔn)疥P(guān)系將處理邏輯模塊化,實(shí)現(xiàn)解耦和靈活擴(kuò)展,本文將探討如何結(jié)合SpringBoot實(shí)現(xiàn)職責(zé)鏈模式,優(yōu)化電商訂單處理流程2024-10-10
Mybatis傳list參數(shù)調(diào)用oracle存儲過程的解決方法
怎么利用MyBatis傳List類型參數(shù)到數(shù)據(jù)庫存儲過程中實(shí)現(xiàn)批量插入數(shù)據(jù)?接下來通過本文給大家介紹Mybatis傳list參數(shù)調(diào)用oracle存儲過程,需要的朋友可以參考下2017-03-03
Log4j定時(shí)打印日志及添加模塊名配置的Java代碼實(shí)例
這篇文章主要介紹了Log4j定時(shí)打印日志及添加模塊名配置的Java代碼實(shí)例,Log4j是Apache的一個開源Java日志項(xiàng)目,需要的朋友可以參考下2016-01-01
Java for each實(shí)現(xiàn)機(jī)制代碼原理解析
這篇文章主要介紹了Java for each實(shí)現(xiàn)機(jī)制代碼原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
SpringBoot集成slf4j2日志配置的實(shí)現(xiàn)示例
本文主要介紹了SpringBoot集成slf4j2日志配置的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08
Java操作MongoDB事務(wù)未生效的常見場景及解決方案
在 Java 開發(fā)中,使用 MongoDB 存儲數(shù)據(jù)時(shí),事務(wù)的正確使用至關(guān)重要,然而,在實(shí)際開發(fā)過程中,經(jīng)常會遇到 MongoDB 事務(wù)沒有生效的情況,本文我將結(jié)合多年實(shí)踐經(jīng)驗(yàn),深入剖析事務(wù)未生效的常見場景,并給出詳細(xì)的解決方案,需要的朋友可以參考下2025-07-07

