最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中for循環(huán)內(nèi)修改集合的常見(jiàn)陷阱與最佳實(shí)踐

 更新時(shí)間:2025年06月16日 08:24:16   作者:碼農(nóng)阿豪@新空間  
在Java編程中,for循環(huán)是遍歷集合(如List、Set)的常用方式,本文主要介紹了Java在for循環(huán)內(nèi)修改集合的常見(jiàn)陷阱與最佳實(shí)踐,希望對(duì)大家有所幫助

1. 引言

在Java編程中,for循環(huán)是遍歷集合(如List、Set)的常用方式。然而,許多開(kāi)發(fā)者在循環(huán)內(nèi)部直接對(duì)集合進(jìn)行增刪改操作時(shí),往往會(huì)遇到ConcurrentModificationException異常。例如:

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
for (Integer num : numbers) {
    if (num % 2 == 0) {
        numbers.remove(num); // 拋出ConcurrentModificationException
    }
}

本文將深入探討Java集合在循環(huán)中修改的問(wèn)題,分析fail-fast機(jī)制,并提供線程安全的修改方案。

2. 問(wèn)題現(xiàn)象:為什么在for循環(huán)中修改集合會(huì)出錯(cuò)?

2.1 典型錯(cuò)誤示例

(1)增強(qiáng)for循環(huán)刪除元素

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
for (String s : list) {
    if (s.equals("B")) {
        list.remove(s); // 拋出ConcurrentModificationException
    }
}

異常原因:Java的for-each循環(huán)使用Iterator,直接修改集合會(huì)導(dǎo)致迭代器狀態(tài)不一致。

(2)普通for循環(huán)刪除元素(可能出錯(cuò))

List<Integer> nums = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
for (int i = 0; i < nums.size(); i++) {
    if (nums.get(i) % 2 == 0) {
        nums.remove(i); // 可能導(dǎo)致元素跳過(guò)
    }
}
// 結(jié)果可能是 [1, 3, 4] 而非預(yù)期的 [1, 3]

問(wèn)題:刪除元素后列表大小變化,但循環(huán)索引繼續(xù)遞增,導(dǎo)致某些元素被跳過(guò)。

3. 深入分析:Java集合的fail-fast機(jī)制

3.1 什么是fail-fast?

Java的ArrayList、HashSet等非線程安全集合采用fail-fast機(jī)制:

當(dāng)?shù)鳈z測(cè)到集合被并發(fā)修改(即非通過(guò)迭代器自身的方法修改),立即拋出ConcurrentModificationException。

目的是快速失敗,避免潛在的數(shù)據(jù)不一致問(wèn)題。

3.2 源碼分析

以ArrayList為例,其Iterator實(shí)現(xiàn)會(huì)檢查modCount(修改計(jì)數(shù)器):

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

modCount:集合結(jié)構(gòu)修改次數(shù)(如add、remove)。

expectedModCount:迭代器預(yù)期的修改次數(shù)。

直接調(diào)用list.remove()會(huì)修改modCount,導(dǎo)致與expectedModCount不一致。

4. 解決方案:安全修改集合的幾種方法

4.1 方法1:使用Iterator的remove()方法(推薦)

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
    Integer num = it.next();
    if (num % 2 == 0) {
        it.remove(); // 安全刪除
    }
}
System.out.println(numbers); // [1, 3]

優(yōu)點(diǎn):

迭代器自身維護(hù)modCount,不會(huì)觸發(fā)異常。

適用于單線程環(huán)境。

4.2 方法2:使用Java 8+的removeIf()

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
numbers.removeIf(num -> num % 2 == 0);
System.out.println(numbers); // [1, 3]

優(yōu)點(diǎn):

代碼簡(jiǎn)潔,內(nèi)部使用Iterator實(shí)現(xiàn)。

性能較好。

4.3 方法3:使用CopyOnWriteArrayList(線程安全)

List<Integer> numbers = new CopyOnWriteArrayList<>(Arrays.asList(1, 2, 3, 4));
for (Integer num : numbers) {
    if (num % 2 == 0) {
        numbers.remove(num); // 安全但性能較低
    }
}
System.out.println(numbers); // [1, 3]

適用場(chǎng)景:

多線程環(huán)境。

缺點(diǎn):每次修改會(huì)復(fù)制整個(gè)數(shù)組,性能較差。

4.4 方法4:普通for循環(huán)反向遍歷

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
for (int i = numbers.size() - 1; i >= 0; i--) {
    if (numbers.get(i) % 2 == 0) {
        numbers.remove(i); // 避免索引錯(cuò)位
    }
}
System.out.println(numbers); // [1, 3]

優(yōu)點(diǎn):

無(wú)需額外迭代器或副本。

適用于簡(jiǎn)單刪除邏輯。

4.5 方法5:記錄待刪除元素,最后批量刪除

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
List<Integer> toRemove = new ArrayList<>();
for (Integer num : numbers) {
    if (num % 2 == 0) {
        toRemove.add(num);
    }
}
numbers.removeAll(toRemove);
System.out.println(numbers); // [1, 3]

適用場(chǎng)景:

需要復(fù)雜條件判斷時(shí)。

缺點(diǎn):需要額外空間存儲(chǔ)待刪除元素。

5. 性能對(duì)比:不同方法的效率分析

方法時(shí)間復(fù)雜度空間復(fù)雜度線程安全適用場(chǎng)景
Iterator.remove()O(n)O(1)單線程推薦
removeIf()O(n)O(1)Java 8+簡(jiǎn)潔寫法
CopyOnWriteArrayListO(n²)O(n)多線程環(huán)境
反向遍歷O(n)O(1)簡(jiǎn)單刪除邏輯
記錄后批量刪除O(n)O(n)復(fù)雜刪除條件

結(jié)論:

單線程下優(yōu)先選擇Iterator.remove()或removeIf()。

多線程環(huán)境使用CopyOnWriteArrayList或加鎖。

大數(shù)據(jù)量避免CopyOnWriteArrayList,選擇Iterator或反向遍歷。

6. 最佳實(shí)踐總結(jié)

禁止在增強(qiáng)for循環(huán)中直接修改集合,改用Iterator.remove()。

Java 8+推薦removeIf(),代碼更簡(jiǎn)潔。

多線程環(huán)境使用并發(fā)集合(如CopyOnWriteArrayList)或同步塊。

大規(guī)模數(shù)據(jù)刪除優(yōu)先選擇Iterator或反向遍歷。

復(fù)雜條件刪除可先記錄元素,再批量刪除。

7. 結(jié)論

在Java中,直接于for循環(huán)內(nèi)修改集合會(huì)觸發(fā)ConcurrentModificationException,根源在于fail-fast機(jī)制。
安全修改集合的最佳實(shí)踐包括:

  • 單線程:Iterator.remove()或removeIf()
  • 多線程:CopyOnWriteArrayList或同步控制

掌握這些方法后,可以避免常見(jiàn)陷阱,寫出更健壯的Java代碼。 

到此這篇關(guān)于Java中for循環(huán)內(nèi)修改集合的常見(jiàn)陷阱與最佳實(shí)踐的文章就介紹到這了,更多相關(guān)Java for循環(huán)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 教你怎么用idea創(chuàng)建web項(xiàng)目

    教你怎么用idea創(chuàng)建web項(xiàng)目

    好多朋友在使用IDEA創(chuàng)建項(xiàng)目時(shí),總會(huì)碰到一些小問(wèn)題.現(xiàn)在我們就演示一下使用IDEA創(chuàng)建web項(xiàng)目的完整步驟吧.文中有非常詳細(xì)的圖文示例哦,,需要的朋友可以參考下
    2021-05-05
  • 讓Java代碼更高效

    讓Java代碼更高效

    這篇文章主要介紹了如何讓你的Java代碼更高效,讓Java跑得更快,感興趣的小伙伴們可以參考一下
    2015-09-09
  • Java實(shí)現(xiàn)AWT四大事件的詳細(xì)過(guò)程

    Java實(shí)現(xiàn)AWT四大事件的詳細(xì)過(guò)程

    AWT的事件處理是一種委派式事件處理方式:普通組件(事件源)將整個(gè)事件處理委托給特定的對(duì)象(事件監(jiān)聽(tīng)器);當(dāng)該事件源發(fā)生指定的事件時(shí),就通知所委托的事件監(jiān)聽(tīng)器,由事件監(jiān)聽(tīng)器來(lái)處理這個(gè)事件
    2022-04-04
  • Java 使用反射調(diào)用jar包中的類方式

    Java 使用反射調(diào)用jar包中的類方式

    這篇文章主要介紹了Java 使用反射調(diào)用jar包中的類方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • Java如何接收并解析HL7協(xié)議數(shù)據(jù)

    Java如何接收并解析HL7協(xié)議數(shù)據(jù)

    文章主要介紹了HL7協(xié)議及其在醫(yī)療行業(yè)中的應(yīng)用,詳細(xì)描述了如何配置環(huán)境、接收和解析數(shù)據(jù),以及與前端進(jìn)行交互的實(shí)現(xiàn)方法,文章還分享了使用7Edit工具進(jìn)行調(diào)試的經(jīng)驗(yàn),并記錄了一個(gè)常見(jiàn)的解析問(wèn)題及其解決方法
    2024-12-12
  • 使用@TableField(updateStrategy=FieldStrategy.IGNORED)遇到的坑記錄

    使用@TableField(updateStrategy=FieldStrategy.IGNORED)遇到的坑記錄

    這篇文章主要介紹了使用@TableField(updateStrategy=FieldStrategy.IGNORED)遇到的坑及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • MyBatis-Plus邏輯刪除實(shí)現(xiàn)過(guò)程

    MyBatis-Plus邏輯刪除實(shí)現(xiàn)過(guò)程

    本文介紹了MyBatis-Plus如何實(shí)現(xiàn)邏輯刪除功能,包括自動(dòng)填充字段、配置與實(shí)現(xiàn)步驟、常見(jiàn)應(yīng)用場(chǎng)景,并展示了如何使用remove方法進(jìn)行邏輯刪除,邏輯刪除通過(guò)修改字段值來(lái)標(biāo)記數(shù)據(jù)為刪除狀態(tài),保留數(shù)據(jù)歷史記錄,避免數(shù)據(jù)丟失
    2025-12-12
  • 使用Java實(shí)現(xiàn)數(shù)組的逆序輸出的幾種方法

    使用Java實(shí)現(xiàn)數(shù)組的逆序輸出的幾種方法

    本文介紹了Java中將數(shù)組逆序輸出的幾種方法,包括使用循環(huán)、內(nèi)置方法和雙指針,通過(guò)示例代碼和解釋,展示了每種方法的實(shí)現(xiàn)細(xì)節(jié)和運(yùn)行結(jié)果,需要的朋友可以參考下
    2026-03-03
  • Spring?Bean的作用域具體實(shí)現(xiàn)(單例、多例、請(qǐng)求、會(huì)話、Application)

    Spring?Bean的作用域具體實(shí)現(xiàn)(單例、多例、請(qǐng)求、會(huì)話、Application)

    文章講解了Spring?IoC/?DI中Bean作用域的管理方式,涵蓋單例(singleton)、原型(prototype)及Web環(huán)境的request、session、application作用域,通過(guò)注解和配置區(qū)分不同作用域的實(shí)例創(chuàng)建規(guī)則,并通過(guò)測(cè)試驗(yàn)證其生命周期與共享特性,感興趣的朋友一起看看吧
    2025-08-08
  • java將一個(gè)目錄下的所有文件復(fù)制n次

    java將一個(gè)目錄下的所有文件復(fù)制n次

    這篇文章主要為大家詳細(xì)介紹了java將一個(gè)目錄下的所有文件復(fù)制n次,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-08-08

最新評(píng)論

黄梅县| 左云县| 措勤县| 花莲县| 浦东新区| 绵阳市| 抚顺市| 怀宁县| 富锦市| 杭州市| 集安市| 滨海县| 喀喇沁旗| 永泰县| 香河县| 库车县| 蒙阴县| 彩票| 许昌县| 舒兰市| 衡南县| 巍山| 米脂县| 溧水县| 周口市| 左贡县| 抚顺市| 桂阳县| 鄂伦春自治旗| 哈巴河县| 前郭尔| 耒阳市| 兰坪| 台州市| 平舆县| 辰溪县| 彭州市| 玉树县| 同仁县| 福贡县| 常德市|