foreach中移除(remove)元素 问题汇总

1,ArrayList 移除元素时,有时候报错,有时候不报错

List a = new ArrayList<>();
        a.add("1");
        a.add("2");
        for (String temp : a) {
            if("1".equals(temp)){
                a.remove(temp);
            }
        }

上面代码在编译和运行时都不会报错,并且也能正常remove掉元素,但是如果将"1" 换成"2",运行时就会报ConcurrentModificationException.首选需要分析ArrayList的源码,ArrayList类继承自AbstractList类,该类中有一个成员变量:

protected transient int modCount = 0;

modCount用于记录变更次数,当add或者remove时,都会自增1

foreach时会调用ArrayList的iterator方法,该返回一个Itr类型的内部类,该内部类有几个需要注意的成员变量:

int cursor;       // index of next element to return
int expectedModCount = modCount;

cursor用于记录获取到哪个元素,expectedModCount用于记录变更次数,如果在iter过程中删除了一个元素,在调用iter.next()时会调用 Itr.checkForComdification()方法校验是否有变更:

final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

如果expectedModCount != modCount,就会报ConcurrentModificationException异常.

现在我们开始分析代码执行过程:

  1. 执行itr类的hasNext判断是否还有元素,此时a的size(ArrayList的成员变量,用于记录元素个数)是2,cursor是0,还有下一个,执行itr.next()
  2. itr.next()判断expectedModCount 是否等于modCount,此时是等于的,继续下一步
  3. 这个时候执行if判断,满足条件,调用arrayList的remove方法,此时modCount+1了,size -1(等于1了)和expectedModCount不等了
  4. 执行itr类的hasNext判断是否还有元素,此时a的size是1,cursor是1,没有下一个,循环结束

如果将"1",换成"2",我们继续分析一下代码执行过程:

  1. 执行itr类的hasNext判断是否还有元素,此时a的size是2(ArrayList的成员变量,用于记录元素个数),cursor是0,还有下一个,执行itr.next()
  2. itr.next()判断expectedModCount 是否等于modCount,此时是等于的,继续下一步
  3. if判断,不满足,继续下一次循环
  4. 执行itr类的hasNext判断是否还有元素,此时a的size是2,cursor是1,还有下一个,执行itr.next()
  5. itr.next()判断expectedModCount 是否等于modCount,此时是等于的,继续下一步
  6. if判断,满足条件,开始移除元素,此时,modCount +1 ,size -1
  7. 执行itr类的hasNext判断是否还有元素,此时a的size是1,cusor是2,还会再次执行一次itr.next()
  8. expectedModCount不等于modCount,报错

2,HashSet 类remove元素时,不报错也没有一处成功

import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;

public class HashCodeTest {
    private int hashCode = 0;

    @Override public int hashCode() {
        return hashCode ++;
    }

    public static void main(String[] args) {
        Set set = new HashSet();

        set.add(new HashCodeTest());
        System.out.println(set.size());
        for (Iterator iter = set.iterator();
                iter.hasNext();) {
            iter.next();
            iter.remove();
        }
        System.out.println(set.size());
    }
}
上面代码在remove时,不报错也没有移除成功,原因是HashSet内部使用 HashMap的key存储数据,remove时会校验key的hashCode和equals方法,如果发生过变化,则无法成功移除元素


你可能感兴趣的:(JAVA)