我们一般使用ArryList做循环遍历的时候,会删除某个元素。
以下是我们的list :
ArryList
List<String> list=new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
list.add("f");
list.add("g");
输出list
for(String s1:list){
System.err.println(s1);
}
一下是两种做法:
做法1:
for(int i=0;i<list.size();i++){
if(list.get(i).equals("c")){
list.remove(i);
}
}
输出结果:
a
b
d
e
f
g
做法2:
for(String s1:list){
if(s1.equals("c")){
list.remove(s1);
}
}
输出结果:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at Test.main(Test.java:27)
做法3:
Iterator it=list.iterator();
while(it.hasNext()){
if(it.next().equals("c")){
it.remove();
}
}
输出结果:
a
b
d
e
f
g
做法4 :
List<String> deleteList=new ArrayList<>();
for(String s1:list){
if(s1.equals("c")){
deleteList.add(s1);
}
}
list.removeAll(deleteList);
输出结果:
a
b
d
e
f
g
以上,我们可以看到 做法1 3 是不会抛异常 做法2 抛了个异常。 做法4 是另一种解决问题的方式
在我们以后的开发中 使用 1 3 4 来删除list中特定的元素。
现在让我们来看看 异常产生的原因。
做法2 我们使用了foreach 的写法。 这个是 Iterator 接口提供的 快速遍历方式,对比做法1 发现这种写法很简单 很好记。
iterator 有四个 方法:
1 boolean hasNext();
2 E next();
3 default void remove() {
throw new UnsupportedOperationException("remove");
}
4 default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
我们的ArryList 有一个内部类 Itr 实现了 iterator 接口。 分别 重写 了
hasNext next remove forEachRemaining 方法
itr 内部类 部分:
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;
而我们 使用的 foreach 主要使用的是 hasNext next 方法
先看看 两种的具体实现
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];
}
可以看到 在每次的next中 都有一个 :
checkForComodification(); //方法
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
在使用 foreach 遍历的时 内部类中会有 int expectedModCount = modCount;
modCount 表示修改次数。 如果在遍历的时候 删除了元素 做法2
则modCount 的值会改变。 此时 在调用 next 的时候会出现 expectedModCount != modCount
则会抛出异常。
而如果使用 内部的 remove 方法 在romove 中会有
expectedModCount = modCount;
则不会抛异常。