中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中ArrayList陷阱實例分析

發布時間:2022-02-24 09:13:35 來源:億速云 閱讀:167 作者:iii 欄目:開發技術

這篇文章主要介紹“Java中ArrayList陷阱實例分析”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Java中ArrayList陷阱實例分析”文章能幫助大家解決問題。

    問題分析

    疑惑滿滿

    小楓聽到這個面試題的時候,心想這是什么水面試官,怎么問這么簡單的題目,心想一個for循環加上equal判斷再刪除不就完事了嗎?但是轉念一想,不對,這里面肯定有陷阱,不然不會問這么看似簡單的問題。小楓突然想起來之前寫代碼的時候好像遇到過這個問題,也是在ArrayList中刪除指定元素,但是直接for循環remove元素的時候還拋出了異常,面試官的陷阱估計在這里。小楓暗自竊喜,找到了面試官埋下的陷阱。 小楓回想起當天的的測試情況,代碼進行了脫敏改造。當初是要在ArrayList中刪除指定元素,小楓三下五除二,酣暢淋漓的寫下了如下的代碼,信心滿滿的點了Run代碼的按鈕,結果尷尬了,拋異常了。

    public class TestListMain {
    
        public static void main(String[] args) {
    
            List<String> result = new ArrayList<>();
            result.add("a");
            result.add("b");
            result.add("c");
            result.add("d");
    
            for (String s : result) {
                if ("b".equals(s)) {
                    result.remove("b");
                }
            }
    
        }
    }

    一個大大紅色的異常馬上就出來了,OMG,怎么會這樣呢,感覺代碼沒什么問題啊,趕緊看看拋了什么異常,在哪里拋的異常吧。可以看出來拋了一個ConcurrentModificationException的異常,而且是在Itr這個類中的一個檢測方法中拋出來的異常,這是怎么回事呢?我們的原始代碼中并沒有這個Itr代碼,真是百思不得其解。

    Java中ArrayList陷阱實例分析

    撥云見日

    既然從源代碼分析不出來,我們就看下源代碼編譯后的class文件中的內容是怎樣的吧,畢竟class文件才是JVM真正執行的代碼,不看不知道,一看嚇一跳,JDK原來是這么玩的。原來如此,我們原始代碼中的for-each語句,編譯后的實際是以迭代器來代替執行的。

    public class TestListMain {
        public TestListMain() {
        }
    
        public static void main(String[] args) {
            List<String> result = new ArrayList();
            result.add("a");
            result.add("b");
            result.add("c");
            result.add("d");
            //創建迭代器
            Iterator var2 = result.iterator();
    
            while(var2.hasNext()) {
                String s = (String)var2.next();
                if ("b".equals(s)) {
                    result.remove("b");
                }
            }
    
        }
    }

    通過ArrayList創建的Itr這個內部類迭代器,于是for-each循環就轉化成了迭代器加while循環的方式,原來看上去的for-each循環被掛羊頭賣狗肉了。

      public Iterator<E> iterator() {
            return new Itr();
        }

    Itr這個內部類迭代器,通過判斷hasNext()來判斷迭代器是否有內容,而next()方法則獲取迭代器中的內容。

     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;
    
            Itr() {}
    
            public boolean hasNext() {
                return cursor != size;
            }
    
            @SuppressWarnings("unchecked")
            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];
            }
         ...
         
     }

    大致的過程如下所示:

    Java中ArrayList陷阱實例分析

    真正拋異常的地方是這個檢測方法, 當modCount與expectedModCount不相等的時候直接拋出異常了。那我們要看下modCount以及expectedModCount分別是什么。這里的modCount代表ArrayList的修改次數,而expectedModCount代表的是迭代器的修改次數,在創建Itr迭代器的時候,將modCount賦值給了expectedModCount,因此在本例中一開始modCount和expectedModCount都是4(添加了四次String元素)。但是在獲取到b元素之后,ArrayList進行了remove操作,因此modCount就累加為5了。因此在進行檢查的時候就出現了不一致,最終導致了異常的產生。到此我們找到了拋異常的原因,循環使用迭代器進行循環,但是操作元素卻是使用的ArrayList操作,因此迭代器在循環的時候發現元素被修改了所以拋出異常。

    Java中ArrayList陷阱實例分析

    我們再來思考下,為什么要有這個檢測呢?這個異常到底起到什么作用呢?我們先來開下ConcurrentModificationException的注釋是怎么描述的。簡單理解就是不允許一個線程在修改集合,另一個線程在集合基礎之上進行迭代。一旦檢測到了這種情況就會通過fast-fail機制,拋出異常,防止后面的不可知狀況。

    /**
     ***
     * For example, it is not generally permissible for one thread to modify a Collection
     * while another thread is iterating over it.  In general, the results of the
     * iteration are undefined under these circumstances.  Some Iterator
     * implementations (including those of all the general purpose collection implementations
     * provided by the JRE) may choose to throw this exception if this behavior is
     * detected.  Iterators that do this are known as <i>fail-fast</i> iterators,
     * as they fail quickly and cleanly, rather that risking arbitrary,
     * non-deterministic behavior at an undetermined time in the future.
     ***
    **/
    public class ConcurrentModificationException extends RuntimeException {
        ...
    }

    如何正確的刪除

    既然拋異常的原因是循環使用了迭代器,而刪除使用ArrayList導致檢測不通過。那么我們就循環使用迭代器,刪除也是用迭代器,這樣就可以保證一致了。

    public class TestListMain {
    
        public static void main(String[] args) {
    
            List<String> result = new ArrayList<>();
            result.add("a");
            result.add("b");
            result.add("c");
            result.add("d");
    
           Iterator<String> iterator = list.iterator();
     
    		while (iterator .hasNext()) {
    			String str = iterator.next();
    			if ("b".equals(str)) {
    				iterator.remove();
    			}
        }
    }

    關于“Java中ArrayList陷阱實例分析”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    江津市| 应城市| 临猗县| 镇宁| 新竹市| 股票| 区。| 彝良县| 个旧市| 崇义县| 屏南县| 策勒县| 龙门县| 襄垣县| 兴安县| 建湖县| 临夏市| 大冶市| 健康| 三明市| 水富县| 元氏县| 德安县| 红原县| 台南市| 太保市| 府谷县| 唐山市| 苗栗县| 澄城县| 安岳县| 高陵县| 永川市| 安陆市| 潢川县| 利辛县| 泉州市| 托里县| 望奎县| 玛曲县| 宜兴市|