国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java 迭代器引發(fā) ConcurrentModificationException

kumfo / 3484人閱讀

摘要:迭代器智能嗎第一步,將列表中的根節(jié)點(diǎn)找出來。源碼翻開中迭代器的源碼。在迭代器對(duì)象執(zhí)行操作之前,都會(huì)執(zhí)行方法,以判斷當(dāng)前操作下是否安全。

引言

ConcurrentModificationException這個(gè)異常大家都很熟悉,當(dāng)在forEach進(jìn)行刪除時(shí)都會(huì)出現(xiàn)該異常。

如果你還不了解,請(qǐng)參考澍澍的博客:關(guān)于在list循環(huán)的過程中進(jìn)行刪除的處理 - 晨澍的博客

ConcurrentModificationException的解決方案之一是使用迭代器,但是不代表迭代器就一勞永逸了。

使用的時(shí)候還需斟酌數(shù)組的索引。

描述 問題場(chǎng)景

如下圖所示:

原來的同步方法獲取的節(jié)點(diǎn)是節(jié)點(diǎn)的父節(jié)點(diǎn),根據(jù)父節(jié)點(diǎn)進(jìn)行對(duì)應(yīng)。

然而在同步更新文件的時(shí)候,發(fā)現(xiàn)這樣并不好處理,在不改動(dòng)原代碼的情況下,設(shè)計(jì)了將列表轉(zhuǎn)為樹型結(jié)構(gòu)的方法,這樣可以從根節(jié)點(diǎn)向下開始遍歷,便于操作。

也是在牛客網(wǎng)身經(jīng)百戰(zhàn),實(shí)現(xiàn)這個(gè)難度不大。但在編寫相關(guān)實(shí)現(xiàn)的時(shí)候,遇到了一個(gè)小問題。

迭代器智能嗎?

第一步,將列表中的根節(jié)點(diǎn)找出來。

@Override
public ClusterNode listToTree(List clusterNodes) {
    logger.debug("聲明根節(jié)點(diǎn)名稱");
    final String ROOT_NAME = "ROOT";

    logger.debug("聲明根節(jié)點(diǎn)");
    ClusterNode rootNode = null;

    logger.debug("獲取迭代器,遍歷節(jié)點(diǎn)列表");
    Iterator iterator = clusterNodes.iterator();
    while (iterator.hasNext()) {

        logger.debug("向后遍歷");
        ClusterNode clusterNode = iterator.next();

        if (ROOT_NAME.equals(clusterNode.getName())) {
            logger.debug("獲取到根節(jié)點(diǎn),賦值,并從原列表中移除");
            rootNode = clusterNode;
            iterator.remove();
            break;
        }
    }

    logger.debug("設(shè)置子節(jié)點(diǎn)");
    assert rootNode != null;
    setChildrenNode(rootNode, clusterNodes);

    return rootNode;
}

第二步,再從根節(jié)點(diǎn)開始,遞歸設(shè)置子節(jié)點(diǎn)。

/**
 * 為節(jié)點(diǎn)設(shè)置符合條件的子節(jié)點(diǎn),同時(shí)遞歸,設(shè)置子節(jié)點(diǎn)的子節(jié)點(diǎn)
 * @param parentNode   父節(jié)點(diǎn)
 * @param clusterNodes 子節(jié)點(diǎn)列表
 */
private void setChildrenNode(ClusterNode parentNode, List clusterNodes) {
    logger.debug("清空原集合");
    parentNode.getClusterNodes().clear();

    logger.debug("遍歷列表");
    Iterator iterator = clusterNodes.iterator();
    while (iterator.hasNext()) {
        ClusterNode clusterNode = iterator.next();

        logger.debug("如果父節(jié)點(diǎn)匹配");
        if (clusterNode.getParentClusterNode().getName().equals(parentNode.getName())) {
            logger.debug("將當(dāng)前節(jié)點(diǎn)添加到父節(jié)點(diǎn)的子列表中");
            parentNode.getClusterNodes().add(clusterNode);

            logger.debug("移除該節(jié)點(diǎn)");
            iterator.remove();

            logger.debug("遞歸設(shè)置子節(jié)點(diǎn)");
            setChildrenNode(clusterNode, clusterNodes);
        }
    }
}

思想肯定是沒問題的。

調(diào)用了一行遞歸,當(dāng)我在編寫這行代碼的時(shí)候就已經(jīng)察覺到可能不對(duì)了,因?yàn)楸敬蔚鬟€沒有迭代完,條件符合時(shí)就去遞歸,然后遞歸又去新建迭代器,遞歸中又可能刪除新元素,再遞歸回來,繼續(xù)迭代的結(jié)果還正確嗎?

logger.debug("遞歸設(shè)置子節(jié)點(diǎn)");
setChildrenNode(clusterNode, clusterNodes);

本著完全相信JDK的思想,興許我憂慮的事情,其實(shí)大牛們?cè)缇蛶臀医鉀Q了呢?雖然感覺可能不對(duì),但還是這樣寫了。想著把測(cè)試用例寫得完善一些,錯(cuò)了再改唄!

測(cè)試

一測(cè)試,果然出錯(cuò)了!在調(diào)用next方法時(shí)拋出了ConcurrentModificationException異常,看來,迭代器還沒有智能到如此地步。

源碼

翻開ArrayList中迭代器的源碼。(自從上次在慕課網(wǎng)的驅(qū)動(dòng)下,強(qiáng)制自己閱讀了HashMap的源碼后,發(fā)現(xiàn)自己對(duì)讀源碼沒那么抗拒了。)

在刷過編程題后,終于明白為什么這些家公司都問HashMap源碼,HashMap真的是太重要了,可以在實(shí)際業(yè)務(wù)中大大降低算法的時(shí)間復(fù)雜度!

/**
 * Returns an iterator over the elements in this list in proper sequence.
 *
 * 

The returned iterator is fail-fast. * * @return an iterator over the elements in this list in proper sequence */ public Iterator iterator() { return new Itr(); }

迭代器方法內(nèi)部就返回了一個(gè)Itr的對(duì)象,這是ArrayList中的私有內(nèi)部類,為什么要用內(nèi)部類呢?一大好處就是內(nèi)部類可以直接訪問外部類的私有成員,具體進(jìn)行集合操作的時(shí)候非常方便。

ConcurrentModificationException,因?yàn)槭殖R姡晕覀兂3V魂P(guān)注了這個(gè)異常怎么出現(xiàn)的,往往忽略了異常本身。

并發(fā)修改異常,最簡(jiǎn)單的場(chǎng)景,就是線程不安全的多線程并發(fā)場(chǎng)景。

在迭代器對(duì)象執(zhí)行操作之前,都會(huì)執(zhí)行checkForComodification方法,以判斷當(dāng)前操作下是否安全。就像下面的代碼實(shí)現(xiàn)一樣,判斷當(dāng)前的數(shù)量是否是我預(yù)期的數(shù)量。

如果不是,表示有人動(dòng)過我的列表,拋異常,不干了。

如果是,表示沒有人動(dòng)過我的列表,才繼續(xù)執(zhí)行。

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

既然出現(xiàn)了問題,怎么解決呢?

那就不要在遍歷的過程中再去遞歸修改了,等他遍歷完再說。添加一個(gè)待處理的列表,遍歷之后再遞歸,保證每次迭代都不沖突。

logger.debug("下一次需要處理的父節(jié)點(diǎn)");
List nextParentNodes = new ArrayList<>();

logger.debug("遍歷列表");
Iterator iterator = clusterNodes.iterator();
while (iterator.hasNext()) {
    ClusterNode clusterNode = iterator.next();

    logger.debug("如果父節(jié)點(diǎn)匹配");
    if (clusterNode.getParentClusterNode().getName().equals(parentNode.getName())) {
        logger.debug("將當(dāng)前節(jié)點(diǎn)添加到父節(jié)點(diǎn)的子列表中");
        parentNode.getClusterNodes().add(clusterNode);

        logger.debug("移除該節(jié)點(diǎn)");
        iterator.remove();

        logger.debug("添加到下一次父節(jié)點(diǎn)中");
        nextParentNodes.add(clusterNode);
    }
}

logger.debug("遞歸處理所有待處理的父節(jié)點(diǎn)");
for (ClusterNode clusterNode : nextParentNodes) {
    setChildrenNode(clusterNode, clusterNodes);
}
總結(jié)

迭代器并非一勞永逸,保證多次修改的獨(dú)立才是最佳實(shí)踐。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/75264.html

相關(guān)文章

  • Java 迭代引發(fā) ConcurrentModificationException

    摘要:迭代器智能嗎第一步,將列表中的根節(jié)點(diǎn)找出來。源碼翻開中迭代器的源碼。在迭代器對(duì)象執(zhí)行操作之前,都會(huì)執(zhí)行方法,以判斷當(dāng)前操作下是否安全。 引言 ConcurrentModificationException這個(gè)異常大家都很熟悉,當(dāng)在forEach進(jìn)行刪除時(shí)都會(huì)出現(xiàn)該異常。 如果你還不了解,請(qǐng)參考澍澍的博客:關(guān)于在list循環(huán)的過程中進(jìn)行刪除的處理 - 晨澍的博客 showImg(http...

    用戶83 評(píng)論0 收藏0
  • java集合

    摘要:主要用于遍歷集合中的元素,對(duì)象也被稱為迭代器。使用迭代過程中,不可修改集合元素迭代器采用快速失敗機(jī)制。一旦迭代過程中檢測(cè)到該集合已經(jīng)被修改,程序立即出發(fā)異常,而不是顯示修改后的結(jié)果,避免了共享資源而引發(fā)的潛在問題。 集合類和數(shù)組不一樣,數(shù)組元素既可以是基本類型的值,也可以是對(duì)象(實(shí)際上保存的是對(duì)象的引用變量);而集合里只能保存對(duì)象(實(shí)際上只是保存對(duì)象的引用變量,但通常習(xí)慣上認(rèn)為集...

    JinB 評(píng)論0 收藏0
  • Java 集合 Collection、Iterator

    摘要:如果需要?jiǎng)?chuàng)建對(duì)象,則必須與一個(gè)被迭代的集合。這是一個(gè)有狀態(tài)的方法該方法用于保證對(duì)該流的后續(xù)訪問中最大允許訪問的元素個(gè)數(shù)??梢詫?duì)集合元素進(jìn)行整體的聚集操作。 Java集合分為Set(無序、不可重復(fù))、List(有序、重復(fù))、Queue(隊(duì)列)和Map(映射關(guān)系) Java集合概述 數(shù)組元素既可以是基本類型的值,也可以是對(duì)象(實(shí)際保存對(duì)象的引用變量)集合只能保存對(duì)象(實(shí)際保存對(duì)象的引用變量...

    harryhappy 評(píng)論0 收藏0
  • fail-fast與fail-safe在Java集合中的應(yīng)用

    摘要:與在迭代器中的設(shè)計(jì)在中,最典型的與就是關(guān)于迭代器的設(shè)計(jì)。缺點(diǎn)是,迭代器不能正確及時(shí)的反應(yīng)集合中的內(nèi)容,而且一定程度上也增加了內(nèi)存的消耗。 fail-fast與fail-safe簡(jiǎn)介 如果一個(gè)系統(tǒng),當(dāng)有異?;蛘咤e(cuò)誤發(fā)生時(shí)就立即中斷執(zhí)行,這種設(shè)計(jì)稱之為fail-fast。相反如果我們的系統(tǒng)可以在某種異?;蛘咤e(cuò)誤發(fā)生時(shí)繼續(xù)執(zhí)行,不會(huì)被中斷,這種設(shè)計(jì)稱之為fail-safe。 fail-fas...

    Drummor 評(píng)論0 收藏0
  • java集合--Collection接口

    摘要:集合的元素個(gè)數(shù)為輸出集合的元素個(gè)數(shù)為在本代碼中,新建一個(gè)局部變量保存的成員方法返回的值,輸出得到因?yàn)橹挥幸粋€(gè)元素。注若遍歷集合的同時(shí)改變集合,將引發(fā)異常。 ????在概述里面也說過:Collection是java集合兩大接口之一,旗下有三大子接口:Set(元素不能重復(fù),且無序)、Queue、List(元素可重復(fù),且有序)。????Collection來源于java.util包,主要方法...

    k00baa 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<