For summary for, foreach and iterator (for collection of deleted elements)

Foreword

  For the difference for, foreach, iterators, not described here in detail. Only a simple introduction, what happens when Benpian elements for circulation to delete the collection.

Brief difference

  Directly on the code:

   public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("dabin");
        list.add("xiaobin");
        list.add("datoubin");
        list.add("dabin");
        list.add("dabin");
    System.out.println("foreach写法");
  for (String it : list) {
System.out.println(it);
  }
   System.out.println("for写法");
  for (int i = 0; i < list.size(); i++) {
   System.out.println(list.get(i));
  }
  System.out.println("迭代器写法");
  Iterator<String> it = list.iterator();
   while (it.hasNext()) {
   System.out.println(it.next());
  }
  }
 

  FIG output:

              

   So we see the difference used,

  Then is the focus of this article! ! ! !

Print collection cycle remove elements problem

   For example, I just want to delete some elements from the collection are three ways to get them to explain:

  Common for loop through elements deleted

    Directly on the code:

 1 public static void main(String[] args) {
 2         List<String> list = new ArrayList<>();
 3         list.add("dabin");
 4         list.add("xiaobin");
 5         list.add("datoubin");
 6         list.add("dabin");
 7         list.add("dabin");
 8         for (int i = 0; i < list.size(); i++) {
 9             if ("dabin".equals(list.get(i))) {
10                 list.remove(i);
11             }
12         }
13         for (int i = 0; i < list.size(); i++) {
14             System.out.println(list.get(i));
15         }
16 }

 

    This time Output:

    

    Hey, we found out why there is a dabin not deleted it?

    This will delete from the for statement in terms of,

    This is because after the for loop iterates through the list to delete the elements, the elements behind the front bench position at the front of the element, so there may result in traversal process certain elements are missing, the results appeared above it kinds of results.

    Obviously this way is not precisely set delete all the elements to meet the conditions, have also said that because the element position changes led to this situation, so can we put it another thought, descending for loop through it? ? The above code was changed to reverse traversal, we try Kankan:

 1     public static void main(String[] args) {
 2         List<String> list = new ArrayList<>();
 3         list.add("dabin");
 4         list.add("xiaobin");
 5         list.add("datoubin");
 6         list.add("dabin");
 7         list.add("dabin");
 8         for (int i = list.size() - 1; i >= 0; i--) {
 9             if ("dabin".equals(list.get(i))) {
10                 list.remove(i);
11             }
12         }
13         for (int i = 0; i < list.size(); i++) {
14             System.out.println(list.get(i));
15         }
16     }

    输出结果如下:

    咦,这时候就发现,成功了。good

   foreach循环遍历中删除元素

    现在就来试试康foreach了:

 1    public static void main(String[] args) {
 2         List<String> list = new ArrayList<>();
 3         list.add("dabin");
 4         list.add("xiaobin");
 5         list.add("datoubin");
 6         list.add("dabin");
 7         list.add("dabin");
 8 
 9         for (String it : list) {
10             if ("dabin".equals(it)) {
11                 list.remove(it);
12             }
13         }
14 }

 

 

呜呼,完蛋报错了,直接抛出了一个ConcurrentModificationException异常,

我们来看看这个这个异常是因为什么:

其实Foreach遍历是通过实现Iterable接口的类通过,那是因为foreach要依赖于Iterable接口返回的Iterator对象,所以从本质上来讲,Foreach其实就是在使用迭代器,在使用foreach遍历时对集合的结构进行修改,和在使用Iterator遍历时对集合结构进行修改本质上是一样的。所以同样的也会抛出异常,执行快速失败机制。

  那这个问题先留着,我们来先看看用迭代器遍历后能不能解决这个问题。

 

 迭代器遍历中删除元素

  直接上代码:

 1 public static void main(String[] args) {
 2         List<String> list = new ArrayList<>();
 3         list.add("dabin");
 4         list.add("xiaobin");
 5         list.add("datoubin");
 6         list.add("dabin");
 7         list.add("dabin");
 8         Iterator<String> it = list.iterator();
 9         while (it.hasNext()) {
10             String a = it.next();
11             if ("dabin".equals(a)) {
12                 list.remove(a);
13             }
14         }
15 }

 

 

     抛出了相同的异常,现在就要去看看源码,分析为什么抛出了

 1   private class Itr implements Iterator<E> {
 2         int cursor;       // index of next element to return
 3         int lastRet = -1; // index of last element returned; -1 if no such
 4         int expectedModCount = modCount;
 5 
 6         Itr() {}
 7 
 8         public boolean hasNext() {
 9             return cursor != size;
10         }
11 
12         @SuppressWarnings("unchecked")
13         public E next() {
14             checkForComodification();
15             int i = cursor;
16             if (i >= size)
17                 throw new NoSuchElementException();
18             Object[] elementData = ArrayList.this.elementData;
19             if (i >= elementData.length)
20                 throw new ConcurrentModificationException();
21             cursor = i + 1;
22             return (E) elementData[lastRet = i];
23         }
24 
25         public void remove() {
26             if (lastRet < 0)
27                 throw new IllegalStateException();
28             checkForComodification();
29 
30             try {
31                 ArrayList.this.remove(lastRet);
32                 cursor = lastRet;
33                 lastRet = -1;
34                 expectedModCount = modCount;
35             } catch (IndexOutOfBoundsException ex) {
36                 throw new ConcurrentModificationException();
37             }
38         }

 

通过查看源码发现原来检查并抛出异常的是checkForComodification()方法。在ArrayList中modCount是当前集合的版本号,每次修改(增、删)集合都会加1;expectedModCount是当前迭代器的版本号,在迭代器实例化时初始化为modCount。我们看到在checkForComodification()方法中就是在验证modCount的值和expectedModCount的值是否相等,所以当你在调用了ArrayList.add()或者ArrayList.remove()时,只更新了modCount的状态,而迭代器中的expectedModCount未同步,因此才会导致再次调用Iterator.next()方法时抛出异常。但是为什么使用Iterator.remove()就没有问题呢?通过源码的第34行发现,在Iterator的remove()中同步了expectedModCount的值,所以当你下次再调用next()的时候,检查不会抛出异常。
使用该机制的主要目的是为了实现ArrayList中的快速失败机制(fail-fast),在Java集合中较大一部分集合是存在快速失败机制的。
快速失败机制产生的条件:当多个线程对Collection进行操作时,若其中某一个线程通过Iterator遍历集合时,该集合的内容被其他线程所改变,则会抛出ConcurrentModificationException异常。
所以要保证在使用Iterator遍历集合的时候不出错误,就应该保证在遍历集合的过程中不会对集合产生结构上的修改。

 

你以为现在就结束了嘛?不不不

 1     public static void main(String[] args) {
 2         List<String> list = new ArrayList<>();
 3         list.add("dabin");
 4         list.add("xiaobin");
 5         list.add("datoubin");
 6         list.add("dabin");
 7         list.add("dabin");
 8         Iterator<String> it = list.iterator();
 9         while (it.hasNext()) {
10             String a = it.next();
11             if ("dabin".equals(a)) {
12                 it.remove();
13             }
14         }
15         for (int i = 0; i < list.size(); i++) {
16             System.out.println(list.get(i));
17         }
18 }

有没有人发现两者有什么不同呢?

 没错啦,这里是通过iterator的remove()方法来进行删除的,而不是通过list.remove()方法来删除,如果还是用list.remove(),依旧会出现上面那种问题。

有兴趣的同学可以去看看哦,

 

 所以现在总结一下,

通过遍历删除元素的话for和迭代器都是可以实现的,只有foreach不行,

 

 

Guess you like

Origin www.cnblogs.com/dblb/p/11601221.html