1,ArrayList 移除元素时,有时候报错,有时候不报错
List<String> 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异常.
现在我们开始分析代码执行过程:
- 执行itr类的hasNext判断是否还有元素,此时a的size(ArrayList的成员变量,用于记录元素个数)是2,cursor是0,还有下一个,执行itr.next()
- itr.next()判断expectedModCount 是否等于modCount,此时是等于的,继续下一步
- 这个时候执行if判断,满足条件,调用arrayList的remove方法,此时modCount+1了,size -1(等于1了)和expectedModCount不等了
- 执行itr类的hasNext判断是否还有元素,此时a的size是1,cursor是1,没有下一个,循环结束
如果将"1",换成"2",我们继续分析一下代码执行过程:
- 执行itr类的hasNext判断是否还有元素,此时a的size是2(ArrayList的成员变量,用于记录元素个数),cursor是0,还有下一个,执行itr.next()
- itr.next()判断expectedModCount 是否等于modCount,此时是等于的,继续下一步
- if判断,不满足,继续下一次循环
- 执行itr类的hasNext判断是否还有元素,此时a的size是2,cursor是1,还有下一个,执行itr.next()
- itr.next()判断expectedModCount 是否等于modCount,此时是等于的,继续下一步
- if判断,满足条件,开始移除元素,此时,modCount +1 ,size -1
- 执行itr类的hasNext判断是否还有元素,此时a的size是1,cusor是2,还会再次执行一次itr.next()
- 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<HashCodeTest> set = new HashSet<HashCodeTest>();
set.add(new HashCodeTest());
System.out.println(set.size());
for (Iterator<HashCodeTest> iter = set.iterator();
iter.hasNext();) {
iter.next();
iter.remove();
}
System.out.println(set.size());
}
}
上面代码在remove时,不报错也没有移除成功,原因是HashSet内部使用HashMap的key存储数据,remove时会校验key的hashCode和equals方法,如果发生过变化,则无法成功移除元素