List刪除元素(fail-fast與fail-safe)

Iterator 接口 也是Java集合框架成員,它主要用來遍歷(即迭代訪問)Collection集合中的元素,Iterator 對象也稱爲迭代器

  • boolean hasNext(): 如果迭代中的集合元素沒有被遍歷完,則返回true

  • Object next(): 返回集合中的下一個元素

  • void remove(): 刪除集合裏上一次next方法返回的元素

Iterator模式是用於遍歷集合類的標準訪問方法。它可以把訪問邏輯從不同類型的集合類中抽象出來,從而避免向客戶端暴露集合的內部結構。

for進行刪除

  1. 可以正常刪除元素
  2. 由於執行list.remove(i),list的長度減少了1,若是直接返回list.get(i)返回的是list.get(i+1)的值
  3. 如果想正常的獲取刪除的值可以先定義String str =null,再用str = list.get(i),最後在執行remove方法就可以獲取刪除元素的值了

list裏面有a,b,c,d,e 元素

@Test
    public void testForRemove() {
        forRemove(list, "b");
        for(String str:list) {
            System.out.print(str+"\t");
        }

    }
    /*
     * 1. list.remove()成功時,list的長度變小一位
     *
     * 2.這裏獲取刪除元素的值,list.get(i)獲取刪除的值,變爲了list.get(i+1)的值
     */
    public static void forRemove(List<String> list,String target){
        for(int i = 0;i<list.size();i++){
            if(list.get(i).equals(target)){
                list.remove(i);
                //本來應該輸出b,但是卻輸出c
                System.out.print(list.get(i)+"\t");

            }

        }

    }

image_1cm7if28g1gbi16s01ho9mn1gk51p.png-6.2kB
forRemove 這種方法,數據量大時,效率特別低

Iterator刪除


    public static void IteratorRemove(List<String> list,String target){
        Iterator<String> iterator = list.iterator();

        while(iterator.hasNext()){
            String s  =  iterator.next();
            if(s.equals(target)){
                iterator.remove();
            }
        }
    }

Itreator 必須依附於Collection 對象,Iterator 迭代器採用的是快速失敗(fail-fast)機制,一旦迭代過程中檢測到該集合已經被修改(通常是程序中的其他線程修改),程序立即引發ConcurrentModificationException異常,而不是顯示修改後的結果,這樣可以避免共享資源引發的潛在問題
注意:

foreach(加強for循環)進行刪除會出問題

循環迭代訪問元素集合更加便捷,但是foreach循環中迭代變量也不是集合本身,當進行刪除操作時,直接使用list.remove(),並不能在用於刪除集合。對於加強for循環,經過編譯後採用的是迭代器模式,來遍歷,當進行刪除,修改時會引發fail-fast(快速失敗)

 public static void main(String[] args) {

        List<String> list = new ArrayList<>();

        list.add("aaa");
        list.add("bbb");
        list.add("ccc");

        for (String str : list) {
            if(str.equals("aaa")) {
                list.remove("aaa");
            }
        }

    }
}

反編譯後可以看到,foreach使用的

public static void main(String[] args) {
        List<String> list = new ArrayList();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        Iterator var3 = list.iterator();

        while (var3.hasNext()) {
            String str = (String) var3.next();
            if (str.equals("aaa")) {
                list.remove("aaa"); //直接使用list.remove
            }
        }

    }

安全失敗(fail—safe)

    採用安全失敗機制的集合容器,在遍歷時不是直接在集合內容上訪問的,而是先複製原有集合內容,在拷貝的集合上進行遍歷。

    原理:由於迭代時是對原集合的拷貝進行遍歷,所以在遍歷過程中對原集合所作的修改並不能被迭代器檢測到,所以不會觸發ConcurrentModificationException。

    缺點:基於拷貝內容的優點是避免了ConcurrentModificationException,但同樣地,迭代器並不能訪問到修改後的內容,即:迭代器遍歷的是開始遍歷那一刻拿到的集合拷貝,在遍歷期間原集合發生的修改迭代器是不知道的。

    場景:java.util.concurrent包下的容器都是安全失敗,可以在多線程下併發使用,併發修改。

參考1: Hollis直面java第45期
參考2: Java瘋狂講義

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章