Java教程

JAVA中循环删除(remove掉)list中元素的方法总结

Java1000个问题 小海豚博客管理员 2020-06-17 14:49:57.0 58 0条

平常我们在list循环中删除元素时 一不小心就会报java.util.ConcurrentModificationException 错误,那怎么样删除才不会报错呢

1.首先说下有几种循环:

1> for循环遍历list:

  1. for(int i=0;i<list.size();i++){
  2. }

2> 增强for循环

  1. for(String x:list){
  2. }

3> iterator遍历(迭代)

  1. Iterator<String> it = list.iterator();
  2. while(it.hasNext()){
  3. String x = it.next();
  4. }

2. 3种循环在遍历集合时,对集合本身操作的注意点:

——for循环遍历list时,对集合进行add或remove操作

  1. for(int i=0;i<list.size();i++){
  2. if(list.get(i).equals("del"))
  3. list.remove(i);
  4. }

—会报java.util.ConcurrentModificationException 异常

这种方式的问题在于,删除某个元素后,list的大小也发生了变化,而你的索引也在改变,所以会导致你在遍历的时候漏掉某些元素。比如当你删除第1个元素后,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际上访问的是第3个。因此,这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素的使用。

—解决方案:

  1. 1> 倒过来遍历list
  2. for(int i=list.size()-1;i>=0;i--){
  3. list.remove(i);
  4. }
  5. 2> 每移除一个元素以后再把i移过来
  6. for(int i=0;i<list.size();i++){
  7. list.remove(i)
  8. i--;
  9. }

——增强for循环和iterator遍历时,对集合进行add或remove操作

  1. for(String x:list){
  2. list.remove(x);//list.add(x);
  3. }

—iterator(迭代)遍历

  1. Iterator<String> it = list.iterator();
  2. while(it.hasNext()){
  3. String x = it.next();
  4. if(x.equals("del")){
  5. list.remove();//应该用iterator自己的remove方法
  6. }
  7. }

—会报java.util.ConcurrentModificationException //这是一个并发修改异常报错
at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
at java.util.AbstractList$Itr.next(Unknown Source)
异常

1>在遍历集合过程中,增强for循环不能对集合本身进行增删操作!

2>该list每当删除一个元素时,集合的size方法的值都会减小1,这将直接导致集合中元素的索引重新排序,
进一步说,就是剩余所有元素的索引值都减1,而for循环语句的局部变量i仍在递增,这将导致删除操作
发生跳跃。从而导致上述代码的删除出现问题。所以不要在增强for循环里进行元素的remove/add操作!

3>这种方式的问题在于,删除元素后继续循环会报错,因为元素在使用时发生了并发的修改,导致异常抛出。
但是删除完毕马上使用break跳出,则不会触发报错。

  1. for(String x:list){
  2. if(x.equals("del")){
  3. list.remove(x);
  4. break;
  5. }
  6. }

4>在遍历集合时,增强for的遍历是封装了迭代器的试遍历方式!(如下)

5>在AbstractList$Itr这个类中实现了iterator接口,当使用增强的for循环时,应该是使用迭代器
进行迭代了,如果你在这期间使用了add或remove方法的话,在ArrayList类中执行了这样的代码:

  1. public boolean add(E e) {
  2. ensureCapacity(size + 1); // Increments(增长) modCount!!
  3. elementData[size++] = e;
  4. return true;
  5. }
  6. --add中的ensureCapacity(size + 1);的调用代码:
  7. public void ensureCapacity(int minCapacity) {
  8. modCount++;//modCount是AbstractList类中的一个成员变量,该值表示对List的修改次数!
  9. int oldCapacity = elementData.length;
  10. if (minCapacity > oldCapacity) {
  11. Object oldData[] = elementData;
  12. int newCapacity = (oldCapacity * 3)/2 + 1;
  13. if (newCapacity < minCapacity)
  14. newCapacity = minCapacity;
  15. // minCapacity is usually close to size, so this is a win:
  16. elementData = Arrays.copyOf(elementData, newCapacity);
  17. }
  18. }
  19. --这里的modCount增加了,但是迭代器中的next方法时,
  20. public E next() {
  21. checkForComodification();
  22. try {
  23. E next = get(cursor);//cursor:表示下一个要访问的元素的索引
  24. lastRet = cursor++;// lastRet:表示上一个访问的元素的索引
  25. return next;
  26. } catch (IndexOutOfBoundsException e) {
  27. checkForComodification();
  28. throw new NoSuchElementException();
  29. }
  30. }
  31. --先checkForComodification();
  32. final void checkForComodification() {
  33. if (modCount != expectedModCount)//expectedModCount:表示对ArrayList修改次数的期望值,它的初始值为modCount为0。
  34. throw new ConcurrentModificationException();
  35. }
  36. }

—因此抛出了异常。。。
——通俗点讲:
迭代器内部的每次遍历都会记录List内部的modcount当做预期值,然后在每次循环中用预期值与List
的成员变量modCount作比较,但是普通list.remove调用的是List的remove,这时modcount++,
但是iterator内记录的预期值=并没有变化,所以会报错,
—但是如果在iterator中调用remove,这时会同步List的modCount到iterator中,故不再报错。

  1. Iterator<String> it = list.iterator();
  2. while(it.hasNext()){
  3. String x = it.next();
  4. if(x.equals("del")){
  5. it.remove();
  6. }
  7. }

—-再简单点说:

  1. 调用list.remove()方法导致modCountexpectedModCount的值不一致而报异常
暗锚,解决锚点偏移

文章评论

嘿,来试试登录吧!