前言------
上课时老师稍微讲了下循环删除元素只有一种方式可以,其他会错,但是也就是这么说一下。。好奇的我,趁这篇博文,整理了3种方式,并分析了为啥可以和为啥不可以,整理得很用心,希望自己理解的同时也能帮到路过的你。
如同我上篇博文提到,循环变量容器可以用for(),foreach, 迭代器遍历这三种,那么是否意味着循环删除List中的元素也可以有这三种方式呢?想起来貌似可以,毕竟删除离不开循环鸭。我们就三种方式都来试试。
目录
一.for循环删除List中的元素
package Test;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import Charactor.String;
public class TestDeleteOfContainer {
List <String> strings = new ArrayList<>();
public static void main(String[] args) {
// TODO Auto-generated method stub
TestDeleteOfContainer myTest = new TestDeleteOfContainer();
myTest.forDelete();
}
public void forDelete() {
String temp ;
//添加100个String对象
for(int i = 0 ;i < 100 ;i++) {
strings.add(new String("s" + i));
}
for(int i = 0 ;i <strings.size();i++) {
strings.remove(i);//满足则删除
}
System.out.println("现在,strings的大小为:" + strings.size());
}
运行结果:现在,strings的大小为:50
怎么回事呢,为啥总共有100个String对象,删除了一遍后,却还剩50个呢?应该要只剩下0个而已啊!!
原因:在for循环删除的过程中,List表的size是随着删除不断改变的。
假如原先List中有1-8个元素,现在remove了箭头所指的第一个元素,因为第一个元素被删了,后面的元素就会一同往前挪一位,原先的2到达了1的位置,原先的3到达了2位置,i++导致箭头往后移,指向了元素3,其实2才是本应该被删除的元素,但是却被遗漏了,这也就是为什么还剩下50个元素没有被删除的原因。所以for循环方式不能正确删除List中的元素
二.迭代器删除List中的元素
package Test;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import Charactor.String;
public class TestDeleteOfContainer {
List <String> strings = new ArrayList<>();
public static void main(String[] args) {
// TODO Auto-generated method stub
TestDeleteOfContainer myTest = new TestDeleteOfContainer();
myTest.iteratorDelete() ;
}
public void iteratorDelete() {
String temp;
//添加100个对象
for(int i = 0 ;i < 100 ;i++) {
strings.add(new String("S"+ i));
}
Iterator it = strings.iterator();
while(it.hasNext()) {
temp = (String)it.next();
if(temp.equals("S8"){
it.remove();
}
}
System.out.println("现在,strings的大小为:" + strings.size());
}
}
运行结果:现在,strings的大小为:99。
上述运行结果说明成功删除。成功原因下面分析,暂且不提。
试想:写成下面这种形式呢?
package Test;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import Charactor.String;
public class TestDeleteOfContainer {
List <String> strings = new ArrayList<>();
public static void main(String[] args) {
// TODO Auto-generated method stub
TestDeleteOfContainer myTest = new TestDeleteOfContainer();
myTest.iteratorDelete() ;
}
public void iteratorDelete() {
String temp;
//添加100个对象
for(int i = 0 ;i < 100 ;i++) {
strings.add(new String("S"+ i));
}
for(Iterator it = strings.iterator(); it.hasNext();) {
temp = (String)it.next();
if(temp.equals("S8")) {
strings.remove(temp);
}
}
System.out.println("现在,strings的大小为:" + strings.size());
}
}
运行结果:出现了java.util.ConcurrentModificationException 异常,这是什么原因呢?这如果要解释的话,必须要涉及Java中List的源码了。不过我先给大家讲一讲大致的原理:
List中有一个modCount的变量,是一个计数变量,计数迭代循环过程中调用了多少次List.add()和List.remove()。
但是List中还有一个expectedModCount的变量,这个变量初始值和modCount一致。每次迭代调用it.next()和it.remove()时都会先判断二者是否相同,如果不相同就会抛出并行修改的异常。为了在修改中不抛出异常,迭代器中有个remove()方法,这个方法本质上也是调用List的remove()方法,虽然每次remove后,modCount的值会+ 1,但是我无只要同步修改expectedModCount 的值就可以了,使得二者永远等同就 可以啦。
因此改成iI.remove就可以啦!!!
说了这么多,是不是没看懂?我放源码啦,自己领悟下:
AbsrtactList中iteraor方法,它返回一个内部类,这个类实现了iterator接口,代码如下:
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor = 0;
int lastRet = -1;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
// 同步修改expectedModCount 的值
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
是不是还没看明白呢?那就总结个干货,直接记起来,迭代循环删除元素中,只能调用迭代器的remove方法,不能调用List的
remove方法,对上面例子来说,就是只能用it.remove(temp),不能用string.remove(temp)~~~~~
三.foreach循环删除List中的元素
package Test;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import Charactor.String;
public class TestDeleteOfContainer {
List <String> strings = new ArrayList<>();
public static void main(String[] args) {
// TODO Auto-generated method stub
TestDeleteOfContainer myTest = new TestDeleteOfContainer();
myTest.foreachDelete();
}
public void foreachDelete() {
//添加100个String对象
for(int i = 0 ;i < 100 ;i++) {
strings.add(new String("s" + i));
}
for(String temp: strings) {
if(temp.equals("S8")) {
strings.remove(temp);
}
}
System.out.println("现在,strings的大小为:" + strings.size());
}
}
运行结果:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at Test.TestDeleteOfContainer.foreachDelete(TestDeleteOfContainer.java:29)
at Test.TestDeleteOfContainer.main(TestDeleteOfContainer.java:16)
也出现了java.util.ConcurrentModificationException 异常,这有是什么原因呢?其实这个原因和上面的一样,都是调用了List中的remove方法了,造成了并行修改的错误。因此foreach修改也不行。
四.干货总结
只能用迭代循环,且过程中只能调用迭代器的remove方法。