前言:
for循環可以刪除集合元素嗎,往往我們得到的答案有時候就是不可以,安全起見,要迭代器,包括我在阿里的開發規範裏也寫了這麼一句話, 不要在 foreach 循環裏進行元素的 remove / add 操作。remove 元素請使用 iterator 方式,如果併發操作,需要對 iterator 對象加鎖
依然記得剛來第三天寫個接口我就for循環內刪除元素,當時很沙雕,恰好又被代碼走查看到了,尷尬的我挖了個洞將for改成了迭代器方式遍歷,這兩天看個大佬的代碼,他就是for循環並remove其中元素,我開心的以為發現了一個bug,嗯,再往下看不對,這代碼妙啊,百度了一下,有了這篇文章 下面我們通過幾個例子以及分析源碼的方式來看看問題,nice
問題一
List<String> list = new ArrayList();
list.add("111");
list.add("222");
list.add("222");
list.add("333");
list.add("222");
list.add("555");
//list.stream().forEach(System.out::println);
for(int i = 0;i < list.size();i++){
if(StrUtil.equals("222",list.get(i))){
list.remove(i);
}}
我們先看下上面這個用例,這個結果是啥呢?是111 222 333 555,咦,明明等於222的移除了啊,怎麼沒移掉,而且還沒報錯,通常我們移除元素會報錯呀,其實這種for方法在我們循環遍歷的時候list.remove(i);會刪除對應的元素不會報錯,但是呢,刪除的元素位置會空出來,後面的元素會往前移一位,這樣如果有兩個元素的位置是連續的話,那麼後面這個元素是不會進行判斷的,這樣就不會符合我們的分析場景的,
我們按代碼順序翻一下,索引在範圍內,則獲取remove的元素,然後將list的元素大小減一,如果還存在,就進行元素的copy,從源數組的index+1位置開始要複製的數組元素的數量numMoved,到目標數組的指定位置,然後通過GC將最後一個位置內存回收,哦。原來是這樣的,至於説的報錯我們下面在分析
問題二
for (String ll : list) {
if(StrUtil.equals(ll,"333")){
list.remove(ll);
}}
如上代碼,當我們使用foreach的時候我們需要remove的是一個對象,而不是for時的下標,這裏會報錯java.util.ConcurrentModificationException,這就是我們説的報錯了,我先把結果説了吧,這裏我們刪除元素的話其實並不會報錯,報錯的是for循環哪裏,在你remove後下一次遍歷的時候才會報錯,報異常的方法是java.util.ArrayList$Itr.checkForComodification,一看就是方法裏的迭代器報錯
- 一個是刪除後元素位置前挪了導致連續相等的元素判斷不到
- 一個是刪除元素後改動的次數變得和期望變動的次數不一樣了導致的這些異常信息
for(int i = list.size()-1;i>=0;i--){
if(StrUtil.equals("222",list.get(i))){
String remove = list.remove(i);
System.out.println("shanchu"+ remove);
}}