欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

實(shí)現(xiàn)Java刪除一個(gè)集合的多個(gè)元素

 更新時(shí)間:2016年08月03日 09:36:21   投稿:daisy  
Java中的For each實(shí)際上使用的是iterator進(jìn)行處理的。而iterator是不允許集合在iterator使用期間刪除的。而我在for each時(shí),從集合中刪除了一個(gè)元素,這導(dǎo)致了iterator拋出了ConcurrentModificationException,下面來(lái)看看到底怎么回事。

問(wèn)題

我需要從一個(gè)java的集合中,根據(jù)另一個(gè)集合的內(nèi)容,刪除第一個(gè)集合中不特定的元素。這看上去非常簡(jiǎn)單,但卻遇到了問(wèn)題。

這是我要寫的方法的頭部

private void screenBlackNameList(List<SharedBoardSmsWrapper> source, List<BlackNameListModel> blackNameList)

事情是這樣子的。source集合中保存了一些顯示用的數(shù)據(jù)元素。blackNameList集合中保存的是黑名單列表。我們需要根據(jù)黑名單表,把source集合中黑名單用戶的數(shù)據(jù)剔除掉。

這個(gè)問(wèn)題的解決看上去非常簡(jiǎn)單。

我首先使用for each 語(yǔ)句進(jìn)行刪除。

for(SharedBoardSmsWrapper tmpSharedBoardSmsWrapper:source){

    for(BlackNameListModel tmpBlackNameListModel:blackNameList){
      if(tmpSharedBoardSmsWrapper.getSource().equals(tmpBlackNameListModel.getSource())){
        source.remove(tmpSharedBoardSmsWrapper);
        break;
      }

    }
  }

非常簡(jiǎn)單的問(wèn)題!我暗笑,

測(cè)試…

令我意外的是,這段代碼居然拋出了異常

java.util.ConcurrentModificationException。

查看JDK6手冊(cè)

public class ConcurrentModificationException
extends RuntimeException

當(dāng)方法檢測(cè)到對(duì)象的并發(fā)修改,但不允許這種修改時(shí),拋出此異常。

例如,某個(gè)線程在 Collection 上進(jìn)行迭代時(shí),通常不允許另一個(gè)線性修改該 Collection。通常在這些情況下,迭代的結(jié)果是不確定的。如果檢測(cè)到這種行為,一些迭代器實(shí)現(xiàn)(包括 JRE 提供的所有通用 collection 實(shí)現(xiàn))可能選擇拋出此異常。執(zhí)行該操作的迭代器稱為 快速失敗 迭代器,因?yàn)榈骱芸炀屯耆。粫?huì)冒著在將來(lái)某個(gè)時(shí)間任意發(fā)生不確定行為的風(fēng)險(xiǎn)。

注意,此異常不會(huì)始終指出對(duì)象已經(jīng)由 不同 線程并發(fā)修改。如果單線程發(fā)出違反對(duì)象協(xié)定的方法調(diào)用序列,則該對(duì)象可能拋出此異常。例如,如果線程使用快速失敗迭代器在 collection 上迭代時(shí)直接修改該 collection,則迭代器將拋出此異常。

注意,迭代器的快速失敗行為無(wú)法得到保證,因?yàn)橐话銇?lái)說(shuō),不可能對(duì)是否出現(xiàn)不同步并發(fā)修改做出任何硬性保證。快速失敗操作會(huì)盡最大努力拋出 ConcurrentModificationException 。因此,為提高此類操作的正確性而編寫一個(gè)依賴于此異常的程序是錯(cuò)誤的做法,正確做法是ConcurrentModificationException  應(yīng)該僅用于檢測(cè) bug。

Java中的For each實(shí)際上使用的是iterator進(jìn)行處理的。而iterator是不允許集合在iterator使用期間刪除的。而我在for each時(shí),從集合中刪除了一個(gè)元素,這導(dǎo)致了iterator拋出了 ConcurrentModificationException

看來(lái)只有老老實(shí)實(shí)使用傳統(tǒng)的for循環(huán)了!

for(int i=0;i<source.size();i++){
    SharedBoardSmsWrapper tmpSharedBoardSmsWrapper=source.get(i);
    for(int j=0;j<blackNameList.size();j++){
      BlackNameListModel tmpBlackNameListModel=blackNameList.get(j);
      if(tmpSharedBoardSmsWrapper.getSource().equals(tmpBlackNameListModel.getSource())){
        source.remove(tmpSharedBoardSmsWrapper);
        break;
      }

    }

  }

這下應(yīng)該沒問(wèn)題了吧!信心滿滿地按下測(cè)試…

暈!怎么回事,數(shù)據(jù)怎么過(guò)濾得不對(duì)?

Debug跟蹤后發(fā)現(xiàn),原來(lái),集合刪除元素時(shí),集合的size會(huì)變小,連帶索引都會(huì)改變!

這可怎么辦?我不會(huì)被這樣一個(gè)小問(wèn)題搞得沒轍了吧!

使用Iterator刪除集合中的元素

查看JDK手冊(cè)的Iterator接口,看到它還有一個(gè)remove方法。

remove

void remove()

從迭代器指向的 collection 中移除迭代器返回的最后一個(gè)元素(可選操作)。每次調(diào)用 next 只能調(diào)用一次此方法。如果進(jìn)行迭代時(shí)用調(diào)用此方法之外的其他方式修改了該迭代器所指向的 collection,則迭代器的行為是不確定的。

拋出:

UnsupportedOperationException - 如果迭代器不支持  remove 操作。

IllegalStateException - 如果尚未調(diào)用  next 方法,或者在上一次調(diào)用  next 方法之后已經(jīng)調(diào)用了  remove 方法。

正確的最終代碼:

/**
   *@paramsource
   *@paramblackNameList
   */
  privatevoid screenBlackNameList(List<SharedBoardSmsWrapper> source, List<BlackNameListModel> blackNameList){
  Iterator<SharedBoardSmsWrapper> sourceIt=source.iterator();

  while(sourceIt.hasNext()){
    SharedBoardSmsWrapper tmpSharedBoardSmsWrapper=sourceIt.next();
    Iterator<BlackNameListModel> blackNameListIt=blackNameList.iterator();
    while(blackNameListIt.hasNext()){
      BlackNameListModel tmpBlackNameListModel=blackNameListIt.next();
      if(tmpSharedBoardSmsWrapper.getSource().equals(tmpBlackNameListModel.getSource())){
        sourceIt.remove();
        break;
      }

    }

  }

  }

注意,一次Iteratornext()方法,不能多次調(diào)用remove()方法。否則會(huì)拋出異常。

看來(lái),刪除集合中的元素,最簡(jiǎn)單的方法,就是使用Iteratorremove()方法了!

讓我們看看ArrayList類提供的Iterator是怎樣實(shí)現(xiàn)的。

privateclass Itr implements Iterator<E> {
  /**
這是元素的索引,相當(dāng)于一個(gè)指針,或者游標(biāo),利用它來(lái)訪問(wèn)List的數(shù)據(jù)元素。
   *Indexofelementtobereturnedbysubsequentcalltonext.
   */
  intcursor = 0;

  /**
   *Indexofelementreturnedbymostrecentcalltonextor
   *previous. Resetto-1ifthiselementisdeletedbyacall
   *toremove.
最新元素的索引。如果已經(jīng)刪除了該元素,就設(shè)為-1
   */
  intlastRet = -1;

  /**
外部類ArrayList的屬性:
protected transient int modCount = 0;
  它用于觀察ArrayList是否同時(shí)在被其他線程修改,如果不一致,那么就會(huì)拋出同步異常。
   *ThemodCountvaluethattheiteratorbelievesthatthebacking
   *Listshouldhave. Ifthisexpectationisviolated,theiterator
   *hasdetectedconcurrentmodification.
   */
  intexpectedModCount = modCount;
//如果游標(biāo)沒有達(dá)到List的尺寸,那么就還有元素。
  publicboolean hasNext() {
      returncursor != size();
  }
//返回當(dāng)前元素,然后游標(biāo)+1。最近索引 也= 返回的元素的索引。
  public E next() {
      checkForComodification();
    try {
    E next = get(cursor);
    lastRet = cursor++;
    return next;
    } catch (IndexOutOfBoundsException e) {
    checkForComodification();
    thrownew NoSuchElementException();
    }
  }
/*
刪除元素,就是刪除當(dāng)前元素,并且把游標(biāo)-1。因?yàn)椋琇ist會(huì)把后面的元素全部移前一位。
*/
  publicvoid remove() {
    if (lastRet == -1)
    thrownew IllegalStateException();
      checkForComodification();

    try {
    AbstractList.this.remove(lastRet);
    if (lastRet < cursor)
      cursor--;
    lastRet = -1;
    expectedModCount = modCount;
    } catch (IndexOutOfBoundsException e) {
    thrownew ConcurrentModificationException();
    }
  }

  finalvoid checkForComodification() {
    if (modCount != expectedModCount)
    thrownew ConcurrentModificationException();
  }
  }

總結(jié)

可以看到,Iterator刪除了元素,并且把游標(biāo)重新置為正確的位子。只要沒有其他線程同時(shí)改變?cè)摷?,就不?huì)有任何問(wèn)題。以上就是本文的全部?jī)?nèi)容了,希望對(duì)大家學(xué)習(xí)Java有所幫助。

相關(guān)文章

  • Java使用agent實(shí)現(xiàn)main方法之前的實(shí)例詳解

    Java使用agent實(shí)現(xiàn)main方法之前的實(shí)例詳解

    這篇文章主要介紹了Java使用agent實(shí)現(xiàn)main方法之前的實(shí)例詳解的相關(guān)資料,希望通過(guò)本文能幫助到大家,讓大家理解這部分內(nèi)容,需要的朋友可以參考下
    2017-10-10
  • jvm添加自定義dns實(shí)現(xiàn)過(guò)程示例

    jvm添加自定義dns實(shí)現(xiàn)過(guò)程示例

    這篇文章主要為大家介紹了jvm添加自定義dns實(shí)現(xiàn)過(guò)程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • java字符串日期類Date和Calendar相互轉(zhuǎn)化及相關(guān)常用方法

    java字符串日期類Date和Calendar相互轉(zhuǎn)化及相關(guān)常用方法

    Java語(yǔ)言的Calendar(日歷),Date(日期),和DateFormat(日期格式)組成了Java標(biāo)準(zhǔn)的一個(gè)基本但是非常重要的部分,下面這篇文章主要給大家介紹了關(guān)于java字符串日期類Date和Calendar相互轉(zhuǎn)化及相關(guān)常用方法的相關(guān)資料,需要的朋友可以參考下
    2023-12-12
  • java制作android 日歷代碼分享

    java制作android 日歷代碼分享

    本文給大家分享的是一段使用java制作Android日歷的代碼,非常簡(jiǎn)單實(shí)用,實(shí)現(xiàn)了讀取日歷事件、插入事件、編輯日歷事件、查看日歷等功能,有需要的小伙伴參考下
    2015-03-03
  • java獲取鍵盤輸入的數(shù)字,并進(jìn)行排序的方法

    java獲取鍵盤輸入的數(shù)字,并進(jìn)行排序的方法

    今天小編就為大家分享一篇java獲取鍵盤輸入的數(shù)字,并進(jìn)行排序的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • SpringBoot與Postman實(shí)現(xiàn)REST模擬請(qǐng)求的操作

    SpringBoot與Postman實(shí)現(xiàn)REST模擬請(qǐng)求的操作

    這篇文章主要介紹了SpringBoot與Postman實(shí)現(xiàn)REST模擬請(qǐng)求的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java 如何遍歷JsonObject對(duì)象

    Java 如何遍歷JsonObject對(duì)象

    這篇文章主要介紹了Java 如何遍歷JsonObject對(duì)象?今天小編就為大家分享一篇Java遍歷JsonObject對(duì)象案例,希望對(duì)大家有所幫助吧
    2021-01-01
  • Java8 使用 stream().sorted()對(duì)List集合進(jìn)行排序的操作

    Java8 使用 stream().sorted()對(duì)List集合進(jìn)行排序的操作

    這篇文章主要介紹了Java8 使用 stream().sorted()對(duì)List集合進(jìn)行排序的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-10-10
  • Springboot整合RabbitMQ消息中間件的使用

    Springboot整合RabbitMQ消息中間件的使用

    在沒有特別要求的情況下,通常會(huì)選擇RabbitMQ作為消息中間件,本文主要介紹了Springboot整合RabbitMQ消息中間件的使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-11-11
  • Java-Io-RandomAccessFile任意位置讀寫數(shù)據(jù)的操作小結(jié)

    Java-Io-RandomAccessFile任意位置讀寫數(shù)據(jù)的操作小結(jié)

    RandomAccessFile類支持隨機(jī)訪問(wèn)方式,可以跳轉(zhuǎn)到文件的任意位置讀寫數(shù)據(jù),這個(gè)類在文件隨機(jī)讀取時(shí)有很大的優(yōu)勢(shì),可利用多線程完成對(duì)一個(gè)大文件的讀寫,本文給大家介紹Java-Io-RandomAccessFile(任意位置讀寫數(shù)據(jù))的相關(guān)知識(shí),需要的朋友可以參考下
    2022-05-05

最新評(píng)論