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

Java面試必備之ArrayList陷阱解析

 更新時(shí)間:2022年02月23日 16:36:43   作者:慕楓技術(shù)筆記  
昨天小楓接到了一個(gè)公司的面試電話,其中一道面試題覺得有點(diǎn)意思,在這里和大家一起分享下。面試題是ArrayList如何刪除指定元素。乍聽很簡(jiǎn)單的問題,但是如果沒有實(shí)際踩過坑很容易掉進(jìn)面試官的陷阱中,我們一起來分析下吧

問題分析

疑惑滿滿

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

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");
            }
        }

    }
}

一個(gè)大大紅色的異常馬上就出來了,OMG,怎么會(huì)這樣呢,感覺代碼沒什么問題啊,趕緊看看拋了什么異常,在哪里拋的異常吧??梢钥闯鰜頀伭艘粋€(gè)ConcurrentModificationException的異常,而且是在Itr這個(gè)類中的一個(gè)檢測(cè)方法中拋出來的異常,這是怎么回事呢?我們的原始代碼中并沒有這個(gè)Itr代碼,真是百思不得其解。

撥云見日

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

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");
        //創(chuàng)建迭代器
        Iterator var2 = result.iterator();

        while(var2.hasNext()) {
            String s = (String)var2.next();
            if ("b".equals(s)) {
                result.remove("b");
            }
        }

    }
}

通過ArrayList創(chuàng)建的Itr這個(gè)內(nèi)部類迭代器,于是for-each循環(huán)就轉(zhuǎn)化成了迭代器加while循環(huán)的方式,原來看上去的for-each循環(huán)被掛羊頭賣狗肉了。

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

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

 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];
        }
     ...
     
 }

大致的過程如下所示:

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

我們?cè)賮硭伎枷?,為什么要有這個(gè)檢測(cè)呢?這個(gè)異常到底起到什么作用呢?我們先來開下ConcurrentModificationException的注釋是怎么描述的。簡(jiǎn)單理解就是不允許一個(gè)線程在修改集合,另一個(gè)線程在集合基礎(chǔ)之上進(jìn)行迭代。一旦檢測(cè)到了這種情況就會(huì)通過fast-fail機(jī)制,拋出異常,防止后面的不可知狀況。

/**
 ***
 * 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 {
    ...
}

回顧整個(gè)過程

如何正確的刪除

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

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();
			}
    }
}

總結(jié)

本文主要對(duì)于ArrayList在for循環(huán)中進(jìn)行元素刪除出現(xiàn)的異常進(jìn)行源碼分析,這也是面試的時(shí)候經(jīng)常出現(xiàn)的面試陷阱題,面試官通過這樣看似簡(jiǎn)單的題目考察候選者的JDK源碼的掌握程度。

真正的大師永遠(yuǎn)懷著一顆學(xué)徒的心

到此這篇關(guān)于Java面試必備之ArrayList陷阱解析的文章就介紹到這了,更多相關(guān)Java ArrayList內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • hadoop之MapReduce框架原理

    hadoop之MapReduce框架原理

    這篇文章主要介紹了hadoop的MapReduce框架原理,MapReduce是分為兩個(gè)階段的,MapperTask階段,和ReduceTask階段。如果有感興趣的小伙伴可以借鑒參考
    2023-03-03
  • 一文詳解Maven的setting文件

    一文詳解Maven的setting文件

    maven?是目前java?常見的一款包管理工具,通過?maven?我們可以很方便的對(duì)項(xiàng)目進(jìn)行編譯、打包、部署等操作,本文將詳細(xì)帶大家了解一下Maven?setting文件,需要的朋友可以參考下
    2024-02-02
  • Java生成范圍內(nèi)隨機(jī)整數(shù)的三種方法

    Java生成范圍內(nèi)隨機(jī)整數(shù)的三種方法

    在Java中生成隨機(jī)數(shù)的場(chǎng)景有很多,下面這篇文章主要給大家介紹了關(guān)于Java生成范圍內(nèi)隨機(jī)整數(shù)的三種方法,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • 詳解Java ArrayList類

    詳解Java ArrayList類

    這篇文章主要介紹了Java ArrayList類的相關(guān)資料,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • Java8 將一個(gè)List<T>轉(zhuǎn)為Map<String,T>的操作

    Java8 將一個(gè)List<T>轉(zhuǎn)為Map<String,T>的操作

    這篇文章主要介紹了Java8 將一個(gè)List<T>轉(zhuǎn)為Map<String, T>的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Spring?Boot項(xiàng)目中使用OpenAI-Java的示例詳解

    Spring?Boot項(xiàng)目中使用OpenAI-Java的示例詳解

    Spring?Boot是由Pivotal團(tuán)隊(duì)提供的全新框架,其設(shè)計(jì)目的是用來簡(jiǎn)化新Spring應(yīng)用的初始搭建以及開發(fā)過程,這篇文章主要介紹了Spring?Boot項(xiàng)目中使用OpenAI-Java的示例詳解,需要的朋友可以參考下
    2023-04-04
  • java的泛型你真的了解嗎

    java的泛型你真的了解嗎

    這篇文章主要為大家詳細(xì)介紹了java的泛型,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • mybatis查詢結(jié)果返回至實(shí)體類的示例代碼

    mybatis查詢結(jié)果返回至實(shí)體類的示例代碼

    這篇文章主要介紹了mybatis查詢結(jié)果返回至實(shí)體類的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • SpringMVC之異常處理解讀

    SpringMVC之異常處理解讀

    這篇文章主要介紹了SpringMVC之異常處理解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Spring框架的環(huán)境搭建和測(cè)試實(shí)現(xiàn)

    Spring框架的環(huán)境搭建和測(cè)試實(shí)現(xiàn)

    這篇文章主要介紹了Spring框架的環(huán)境搭建和測(cè)試實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10

最新評(píng)論