最近,在参加面试的时候遇到考官问这样一个问题,在ArrayList循环遍历的时候删除指定元素是否会出现报错的情况,之前对这个问题没有太过关注,所以回答情况不太理想。后来自己回来研究了一下,发现确实在遍历数组时进行删除存在一些陷阱,废话不多说,直接贴上实践总结代码:
for遍历时删除元素的陷阱
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
/**
* @author yanzy
* @Description ArrayList循环遍历删除元素陷阱--for循环中出现的错误场景分析
* @date 2018-06-22 11:29
*/
public class RemoveByFor {
//ArrayList中fastremove方法执行的操作为数组本身替换,
// 用 删除元素的位置后移一位到原数组结尾位置的内容 替换 删除元素的位置到原数组结尾位置前移一位的内容,
// 最后将数组最后一位设置为null
private List<String> list;
public RemoveByFor(List<String> list){
this.list = list;
}
//错误1:for循环删除ArrayList中的指定值对象,如下写法ArrayList中"bb"只删除了一个,未全部删除
//
//原因:
//由上述可知,删除指定元素最终用到的方法为fastremove,它的实现原理为位置移动,在此处错误场景中,
// 执行第二个元素"bb"删除后,第三个元素"bb"位置前移了一位,
// 在执行循环的下一次操作时,处理的第三个元素为之前的第四位,跳过了对原数组第三个元素"bb"的操作,
// 所以会产生"bb"未删除干净的错误
//
//解决方案:
//倒序遍历进行删除,倒序遍历时,由于删除后数组对象移动的方向与循环遍历操作的方向一致,所以删除操作不会对结果产生影响
/****错误方法****/
public void errorRemove(String deleteVal){
for(int i = 0;i < list.size()-1;i++) {
if(list.get(i).equals(deleteVal)){
list.remove(list.get(i));
}
}
ListIterator listIterator = list.listIterator();
while(listIterator.hasNext()){
System.out.println("---------------------------\t\n");
System.out.println(listIterator.next() + "\t\n");
}
}
/****正确方法****/
public void correctRemove(String deleteVal){
for(int i = list.size()-1;i >= 0;i--) {
if(list.get(i).equals(deleteVal)){
list.remove(list.get(i));
}
}
ListIterator listIterator = list.listIterator();
while(listIterator.hasNext()){
System.out.println("---------------------------\t\n");
System.out.println(listIterator.next() + "\t\n");
}
}
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("bb");
list.add("bb");
list.add("ccc");
list.add("ccc");
list.add("ccc");
RemoveByFor removeByFor = new RemoveByFor(list);
//removeByFor.errorRemove("bb");
removeByFor.correctRemove("bb");
}
}
上述代码,失败情景返回结果:
---------------------------
a
---------------------------
bb
---------------------------
ccc
---------------------------
ccc
---------------------------
ccc
成功情况,返回结果:
---------------------------
a
---------------------------
bb
---------------------------
ccc
---------------------------
ccc
---------------------------
ccc
foreach增强遍历时删除元素的陷阱
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
* @author yanzy
* @Description ArrayList循环遍历删除元素陷阱--foreach循环中出现的错误场景分析
* @date 2018-06-22 13:09
*/
public class RemoveByForeach {
//fastremove方法中,将记录修改数的变量值modCount进行了+1操作,
//ArrayList继承自父类的iterator方法返回的迭代器中的next方法实现中首先进行的操作就是检查迭代器内部修改次数
//checkForComodification(),而ConcurrentModificationException正是在此方法中返回的
//判断条件为modCount != expectedModCount
private List<String> list;
public RemoveByForeach(List<String> list) {
this.list = list;
}
//错误2:foreach循环删除ArrayList中的指定值对象,如下写法出现java.util.ConcurrentModificationException异常
//
//原因:
//由上述可知,ArrayList的remove方法调用的时候会修改modCount值,,触发iterator的fast-fail机制,故出现该异常
//
//解决方案:
//直接使用Iterator中的remove方法进行删除
/****错误方法****/
private void errorRemove(String deleteVal) {
for (String val : list) {
if (val.equals(deleteVal)) {
list.remove(val);
}
}
ListIterator listIterator = list.listIterator();
while(listIterator.hasNext()){
System.out.println("---------------------------\t\n");
System.out.println(listIterator.next() + "\t\n");
}
}
private void correctRemove(String deleteVal){
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
if(iterator.next().equals(deleteVal)){
iterator.remove();
}
}
ListIterator listIterator = list.listIterator();
while(listIterator.hasNext()){
System.out.println("---------------------------\t\n");
System.out.println(listIterator.next() + "\t\n");
}
}
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("bb");
list.add("bb");
list.add("ccc");
list.add("ccc");
list.add("ccc");
RemoveByForeach removeByForeach = new RemoveByForeach(list);
removeByForeach.errorRemove("bb");
//removeByForeach.correctRemove("bb");
}
}
上述代码,失败情况返回结果:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at web.action.RemoveByForeach.errorRemove(RemoveByForeach.java:35)
at web.action.RemoveByForeach.main(RemoveByForeach.java:71)
成功情况返回结果:
---------------------------
a
---------------------------
ccc
---------------------------
ccc
---------------------------
ccc