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

java循環(huán)刪除List元素報(bào)錯(cuò)的原因分析與解決

 更新時(shí)間:2023年11月03日 09:05:02   作者:三火哥  
大家在工作中應(yīng)該都會(huì)遇到從List集合中刪除某一個(gè)或多個(gè)元素的業(yè)務(wù)場(chǎng)景,相信大家都會(huì)避開在循環(huán)里面刪除元素,使用其他方式處理,這是為什么呢,下面小編就來和大家詳細(xì)聊聊

描述

大家在工作中應(yīng)該都會(huì)遇到從List集合中刪除某一個(gè)或多個(gè)元素的業(yè)務(wù)場(chǎng)景

相信大家都會(huì)避開在循環(huán)里面刪除元素,使用其他方式處理

很多面試官也都會(huì)問為什么循環(huán)里面不能刪除元素?

示例

public static void main(String[] args) {  
    List<String> list = new ArrayList<>();  
    list.add("test0");  
    list.add("test1");  
    list.add("test2");  
    list.add("test3");  
    list.add("test4");  
    list.add("test5");  
    System.out.println(list);  

    list.remove(3);  
    System.out.println(list);  

    list.remove("test1");  
    System.out.println(list);  

    for (int i = 0; i < list.size(); i++) {  
        if (i == 2) {  
            list.remove(i);  
        }  
    }  
    System.out.println(list);  

    Iterator<String> it = list.iterator();  
    while (it.hasNext()) {  
        if (it.next().equals("test2")) {  
            it.remove();  
        }  
    }  
    System.out.println(list);  

    for (String s : list) {  
        if ("test5".equals(s)) {  
            list.remove(s);  
        }  
    }  
    System.out.println(list); 
}

//打印結(jié)果
[test0, test1, test2, test3, test4, test5]
[test0, test1, test2, test4, test5]
[test0, test2, test4, test5]
[test0, test2, test5]
[test0, test5]
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.fc.store.Test.main(Test.java:46)

從打印結(jié)果可以看到

  • 根據(jù)索引刪除 --正常
  • 根據(jù)元素刪除 --正常
  • 循環(huán)根據(jù)索引刪除 --正常
  • 迭代刪除原始 --正常
  • 循環(huán)根據(jù)元素刪除 --不正常

執(zhí)行過程

List<String> list = new ArrayList<>();
//集合初始化時(shí) 
transient Object[] elementData;  //空數(shù)組
private int size;  //長度為0
protected transient int modCount = 0;   //修改次數(shù)為0

//添加元素
list.add("test0"); 
elementData[0] = "test0";
size = 1;
modCount = 1;

list.add("test1"); 
elementData[0] = "test0";
elementData[1] = "test1";
size = 2;
modCount = 2;

list.add("test2"); 
elementData[0] = "test0";
elementData[1] = "test1";
elementData[2] = "test2";
size = 3;
modCount = 3;

list.add("test3"); 
elementData[0] = "test0";
elementData[1] = "test1";
elementData[2] = "test2";
elementData[3] = "test3";
size = 4;
modCount = 4;

list.add("test4");
elementData[0] = "test0";
elementData[1] = "test1";
elementData[2] = "test2";
elementData[3] = "test3";
elementData[4] = "test4";
size = 5;
modCount = 5;

list.add("test5");
elementData[0] = "test0";
elementData[1] = "test1";
elementData[2] = "test2";
elementData[3] = "test3";
elementData[4] = "test4";
elementData[5] = "test5";
size = 6;
modCount = 6;

//可以發(fā)現(xiàn)每添加一個(gè)元素,集合size會(huì)增加1, 修改次數(shù)會(huì)增加1

//根據(jù)索引刪除
list.remove(3);
elementData[0] = "test0";
elementData[1] = "test1";
elementData[2] = "test2";
elementData[4] = "test4";
elementData[5] = "test5";
size = 5;
modCount = 7;

//根據(jù)元素刪除
list.remove("test1");
elementData[0] = "test0";
elementData[1] = "test2";
elementData[2] = "test4";
elementData[5] = "test5";
size = 4;
modCount = 8;

//循環(huán)根據(jù)索引刪除
for (int i = 0; i < list.size(); i++) {  
    if (i == 2) {  
        list.remove(i);  
    }  
}
elementData[0] = "test0";
elementData[1] = "test2";
elementData[5] = "test5";
size = 3;
modCount = 9;

//循環(huán)根據(jù)索引刪除
Iterator<String> it = list.iterator();  
while (it.hasNext()) {  
    if (it.next().equals("test2")) {  
        it.remove();  
    }  
}  
elementData[0] = "test0";
elementData[5] = "test5";
size = 2;
modCount = 10;

//可以發(fā)現(xiàn)每刪除一個(gè)元素后,集合size會(huì)減1,修改次數(shù)會(huì)增加1

//循環(huán)根據(jù)元素刪除
for (String s : list) {  
    if ("test2".equals(s)) {  
        list.remove(s);  
    }  
}
//就拋異常了,因?yàn)镮terator的next方法會(huì)校驗(yàn)是否修改
//此時(shí):expectedModCount = 10, modCount = 11

public E next() {
    checkForComodification();
}

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

源碼解析

List 的add和remove方法每次操作都會(huì)對(duì)modCount++

public boolean add(E e) {  
    ensureCapacityInternal(size + 1); // Increments modCount!!  
    elementData[size++] = e;  
    return true;  
}

public E remove(int index) {  
    rangeCheck(index);  
    modCount++;  
    E oldValue = elementData(index);  
    int numMoved = size - index - 1;  
    if (numMoved > 0)  
    System.arraycopy(elementData, index+1, elementData, index,  
    numMoved);  
    elementData[--size] = null; // clear to let GC do its work  
    return oldValue;  
}

public boolean remove(Object o) {  
    if (o == null) {  
        for (int index = 0; index < size; index++)  {
            if (elementData[index] == null) {  
                fastRemove(index);  
                return true;  
            }  
        }
    } else {  
        for (int index = 0; index < size; index++) {
            if (o.equals(elementData[index])) {  
                fastRemove(index);  
                return true; 
            }
        }  
    }  
    return 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; // clear to let GC do its work  
}

private void ensureCapacityInternal(int minCapacity) {  
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));  
}  
  
private void ensureExplicitCapacity(int minCapacity) {  
    modCount++;  
}

Iterator 會(huì)繼承List的modCount,并賦值給expectedModCount

private class Itr implements Iterator<E> {  
    int cursor; // index of next element to return  
    int lastRet = -1; // index of last element returned; -1 if no such  
    int expectedModCount = modCount;
}

Iterator 的next方法會(huì)校驗(yàn)modCount和expectedModCount 是否相同

public E next() {  
    checkForComodification();  
    int i = cursor;  
    if (i >= size)  
        throw new NoSuchElementException();  
    Object[] elementData = ArrayList.this.elementData;  
    if (i >= elementData.length)  
        throw new ConcurrentModificationException();  
    cursor = i + 1;  
    return (E) elementData[lastRet = i];  
}
final void checkForComodification() {  
    if (modCount != expectedModCount)  
        throw new ConcurrentModificationException();  
}

由于在Iterator的便利中使用了List的remove方法,導(dǎo)致modCount增加了 所以在下次next方法中判斷modCount和expectedModCount不一致就直接拋出了異常

解決方案

第一種使用迭代器刪除

Iterator<String> it = list.iterator();  
while (it.hasNext()) {  
    if (it.next().equals("test2")) {  
        it.remove();  
    }  
}  

第二種for循環(huán)刪除后要立即退出

for (String s : list) {  
    if ("test5".equals(s)) {  
        list.remove(s);  
        return;  
    }  
}

//JAVA8語法
list.removeIf("test5"::equals);

Set,Map 同理

同樣Set,Map循環(huán)里面刪除報(bào)錯(cuò)也是同樣的原理

往往大家在遇到問題后,都只是找到了主要原因,但是并沒有找到根本原因。

只有通過深入分析找到根本原因,制定預(yù)防措施(加入CR清單,添加掃碼規(guī)則,制定獎(jiǎng)懲措施),才能夠真正避免問題

到此這篇關(guān)于java循環(huán)刪除List元素報(bào)錯(cuò)的原因分析與解決的文章就介紹到這了,更多相關(guān)java循環(huán)刪除元素內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java面向?qū)ο缶幊蹋ǚ庋b/繼承/多態(tài))實(shí)例解析

    Java面向?qū)ο缶幊蹋ǚ庋b/繼承/多態(tài))實(shí)例解析

    這篇文章主要介紹了Java面向?qū)ο缶幊蹋ǚ庋b/繼承/多態(tài))實(shí)例解析的相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-10-10
  • java字符串壓縮解壓示例

    java字符串壓縮解壓示例

    這篇文章主要介紹了java字符串壓縮解壓示例,先壓縮,再加密,再壓縮,數(shù)據(jù)越大,壓縮比例越高,需要的朋友可以參考下
    2014-03-03
  • 詳解OpenAPI開發(fā)如何動(dòng)態(tài)的添加接口實(shí)現(xiàn)

    詳解OpenAPI開發(fā)如何動(dòng)態(tài)的添加接口實(shí)現(xiàn)

    這篇文章主要為大家介紹了OpenAPI開發(fā)如何動(dòng)態(tài)的添加接口實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • Spring控制Bean加載順序的操作方法

    Spring控制Bean加載順序的操作方法

    正常情況下,Spring 容器加載 Bean 的順序是不確定的,那么我們?nèi)绻枰错樞蚣虞d Bean 時(shí)應(yīng)如何操作?本文將詳細(xì)講述我們?nèi)绾尾拍芸刂?nbsp;Bean 的加載順序,需要的朋友可以參考下
    2024-05-05
  • HashMap在JDK7與JDK8中的實(shí)現(xiàn)過程解析

    HashMap在JDK7與JDK8中的實(shí)現(xiàn)過程解析

    這幾天學(xué)習(xí)了HashMap的底層實(shí)現(xiàn),但是發(fā)現(xiàn)好幾個(gè)版本的,代碼不一,很多文章都是舊版本JDK1.6.JDK1.7的。現(xiàn)在我來分析下JDK7與JDK8中HashMap的實(shí)現(xiàn)過程
    2021-09-09
  • 利用JAVA反射,讀取數(shù)據(jù)庫表名,自動(dòng)生成對(duì)應(yīng)實(shí)體類的操作

    利用JAVA反射,讀取數(shù)據(jù)庫表名,自動(dòng)生成對(duì)應(yīng)實(shí)體類的操作

    這篇文章主要介紹了利用JAVA反射,讀取數(shù)據(jù)庫表名,自動(dòng)生成對(duì)應(yīng)實(shí)體類的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • Java類的訪問權(quán)限關(guān)鍵字用法說明

    Java類的訪問權(quán)限關(guān)鍵字用法說明

    這篇文章主要介紹了Java類的訪問權(quán)限關(guān)鍵字用法說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • SpringBoot?@Configuration與@Bean注解使用介紹

    SpringBoot?@Configuration與@Bean注解使用介紹

    這篇文章主要介紹了SpringBoot中的@Configuration與@Bean注解,在進(jìn)行項(xiàng)目編寫前,我們還需要知道一個(gè)東西,就是SpringBoot對(duì)我們的SpringMVC還做了哪些配置,包括如何擴(kuò)展,如何定制,只有把這些都搞清楚了,我們?cè)谥笫褂貌艜?huì)更加得心應(yīng)手
    2022-10-10
  • springboot實(shí)現(xiàn)用戶名查找用戶功能

    springboot實(shí)現(xiàn)用戶名查找用戶功能

    本文主要介紹了springboot實(shí)現(xiàn)用戶名查找用戶功能,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • IDEA集成Sonar的完整流程

    IDEA集成Sonar的完整流程

    這篇文章主要介紹了IDEA集成Sonar的完整流程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06

最新評(píng)論

霞浦县| 淮安市| 肃北| 峨眉山市| 屏南县| 濮阳县| 涪陵区| 芜湖市| 盐城市| 丰顺县| 宁陕县| 武清区| 册亨县| 广德县| 碌曲县| 枣强县| 和静县| 郁南县| 武平县| 平远县| 尼勒克县| 钟祥市| 镇江市| 夏津县| 龙胜| 肥乡县| 清水县| 苗栗市| 洪湖市| 墨玉县| 丘北县| 通许县| 惠来县| 枞阳县| 玉溪市| 余姚市| 竹北市| 保康县| 夏河县| 肇州县| 华阴市|