大剑无锋之ArrayList中使用增强for循环能删除元素吗?【面试推荐】
好久沒寫java代碼,前幾天面試被問到不少java的問題,其中一個(gè)接下來要說的。
先看幾段代碼。
第一段(集合中兩個(gè)元素,判斷條件是第一個(gè)元素)
ArrayList<String> list = new ArrayList<String>();list.add("george");list.add("georgedage");for (String s : list) {if ("george".equals(s)){list.remove(s);}}System.out.println(list);輸出結(jié)果
[georgedage]?第二段代碼(集合中兩個(gè)元素,判斷條件是第二個(gè)元素)
ArrayList<String> list = new ArrayList<String>();list.add("george");list.add("georgedage");for (String s : list) {if ("georgedage".equals(s)){ list.remove(s);}}System.out.println(list);輸出結(jié)果
再看第三段代碼(集合中三個(gè)元素,判斷條件是第一個(gè)元素)
ArrayList<String> list = new ArrayList<String>();list.add("george");list.add("georgedage");list.add("kangkang");for (String s : list) {if ("george".equals(s)){list.remove(s);}}System.out.println(list);輸出結(jié)果
程序運(yùn)行結(jié)果為:當(dāng)集合中只有兩個(gè)元素,且判斷條件是第一個(gè)元素,remove方法執(zhí)行成功
當(dāng)集合中有兩個(gè)元素,且判斷條件是第二個(gè)元素,remove執(zhí)行拋出ConcurrentModificationException的異常
當(dāng)集合中有兩個(gè)以上元素(不包含兩個(gè))時(shí),判斷條件就算為第一個(gè)元素,remove執(zhí)行也拋出ConcurrentModificationException的異常
或許我們之前編寫代碼,或者看文檔時(shí)有了解過,對(duì)于集合的增加、刪除、修改元素,均不可以使用foreach(增強(qiáng)for循環(huán))
那么為什么呢?
接下來就讓我們走進(jìn)他的內(nèi)心世界!!!(源碼來襲,請(qǐng)睜大雙眼)
在進(jìn)入ArrayList中,我發(fā)現(xiàn)有forEach這個(gè)方法,但是我們都知道增強(qiáng)for循環(huán)對(duì)于集合的話,只適用于實(shí)現(xiàn)Iterable接口的集合上。那么增強(qiáng)for的底層究竟是什么?進(jìn)行一次反編譯。
實(shí)現(xiàn)原理
可以看到,增強(qiáng)For是JAVA提供的語法糖,這里我們剖析一下,這種增強(qiáng)for循環(huán)底層是如何實(shí)現(xiàn)的。
我們對(duì)以下代碼進(jìn)行反編譯:
| 1 2 3 | for (Integer i : list) { ?System.out.println(i); } |
反編譯后:
| 1 2 3 4 | Integer i; for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(i)){ ?i = (Integer)iterator.next(); } |
反編譯后的代碼其實(shí)比較復(fù)雜,我們按照?qǐng)?zhí)行順序拆解一下:
Integer i; 定義一個(gè)臨時(shí)變量i
Iterator iterator = list.iterator(); 獲取List的迭代器
iterator.hasNext(); 判斷迭代器中是否有未遍歷過的元素
i = (Integer)iterator.next(); 獲取第一個(gè)未遍歷的元素,賦值給臨時(shí)變量i
System.out.println(i) 輸出臨時(shí)變量i的值
如此循環(huán)往復(fù),直到遍歷完List中的所有元素。
通過反編譯,我們看到,其實(shí)JAVA中的增強(qiáng)for循環(huán)底層是通過迭代器模式來實(shí)現(xiàn)的。
這也就說通我們上面代碼中所踩的坑
既然增強(qiáng)for循環(huán)通過迭代器實(shí)現(xiàn),那么必然有迭代器的特性。
Java中有fail-fast機(jī)制。在使用迭代器遍歷元素的時(shí)候,在對(duì)集合進(jìn)行刪除的時(shí)候一定要注意,使用不當(dāng)有可能發(fā)生ConcurrentModificationException,這是一種運(yùn)行時(shí)異常,編譯期并不會(huì)發(fā)生。只有在程序真正運(yùn)行時(shí)才會(huì)爆發(fā)。
如以下代碼:
| 1 2 3 4 | for (Student stu : students) { ?if (stu.getId() == 2) ?students.remove(stu); } |
會(huì)拋出ConcurrentModificationException異常。
Iterator是工作在一個(gè)獨(dú)立的線程中,并且擁有一個(gè) mutex 鎖。 Iterator被創(chuàng)建之后會(huì)建立一個(gè)指向原來對(duì)象的單鏈索引表,當(dāng)原來的對(duì)象數(shù)量發(fā)生變化時(shí),這個(gè)索引表的內(nèi)容不會(huì)同步改變,所以當(dāng)索引指針往后移動(dòng)的時(shí)候就找不到要迭代的對(duì)象,所以按照 fail-fast 原則 Iterator 會(huì)馬上拋出java.util.ConcurrentModificationException異常。
所以 Iterator 在工作的時(shí)候是不允許被迭代的對(duì)象被改變的。
在源碼中這樣展示:
private class Itr implements Iterator<E> {int cursor; // index of next element to returnint lastRet = -1; // index of last element returned; -1 if no suchint expectedModCount = modCount;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];}在第一段代碼中,為什么能remove成功呢,其實(shí)它只循環(huán)了一次,所以成功了。
因?yàn)樗趓emove元素1之后,它的size - 1變成1,然后Itr內(nèi)部的cursor變量由0變成1
此時(shí)1 = 1,循環(huán)結(jié)束,所以成功了。
arraylist2為什么remove失敗呢,因?yàn)樗谘h(huán)第二次的時(shí)候,也remove成功了,但是第三次判斷next的時(shí)候cursor的值為2導(dǎo)致不等于現(xiàn)在的size 1,所以執(zhí)行了next方法,最重要的來了,之前remove的操作導(dǎo)致ArrayList的modCount值加1,然后Itr類中的expectedModCount保持不變,所以會(huì)拋出異常。
因此得出結(jié)論:
不允許在foreach中刪除、增加、修改ArrayList中的元素。正確的在遍歷的同時(shí)刪除元素的姿勢(shì):
Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {String item = iterator.next();if ("georgedage".equals(item)){iterator.remove();}}當(dāng)然如果存在并發(fā)操作,還需要對(duì)Iterator進(jìn)行加鎖操作。
撤了撤了,如果還有什么好的建議,互相交流一下!!!
總結(jié)
以上是生活随笔為你收集整理的大剑无锋之ArrayList中使用增强for循环能删除元素吗?【面试推荐】的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 每日两SQL(5),欢迎交流~
- 下一篇: MySQL Explain详解,分析语句