在java集合中调用remove方法后调用iterator.next()报错

  • Post author:
  • Post category:java


  • 例如使用

    Arraylist

    集合返回的一个iterator迭代器,我们

    这样使用会报错

    • ArrayList<Integer> arrayList = new ArrayList<>();
      arrayList.add(1);
      Iterator<Integer> iterator = arrayList.iterator();//获得迭代器
      arrayList.add(2);
      if (iterator.hasNext()) System.out.println(iterator.next());
      

  • 分析

    • 我们来看**

      iterator.next()

      方法源码**:

      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];
      }
      
      final void checkForComodification() {
          if (modCount != expectedModCount)
              throw new ConcurrentModificationException();
      }
      


    • ArrayList


      继承了


      AbstractList



      其中**

      AbstractList

      ** 中有个**

      modCount

      ** 代表了

      集合修改的次数

      。在**

      ArrayList



      iterator



      next()

      方法中会判断**


      expectedModCount





      modCount

      是否相等,如果相等继续执行,不相等报错

    • 这个

      expectedModCount

      是迭代器这个类的一个属性,

      调用

      ArrayList

      自身的

      add

      和“remove

      等改变集合大小的方法都会导致

      modCount+1

      ,但是不会修改

      expectedModCount`

    • 返回的

      迭代器是

      ArrayList

      的一个私有内部类(下文有这个内部类的源码)

      ,在调用集合的iterator()方法返回迭代器的时候底层是new一个新的迭代器返回,它在

      创建的时候会将当前

      modCount

      的值赋给

      expectedModCount

      ,因此创建的时候两者相等


    • 因此我们在创建迭代器后,如果使用了

      ArrayList

      本身的add方法导致

      modCount+1

      后,此时

      modCount



      expectedModCount

      不相等了,再调用迭代器的next()方法就会报错

      • 注意:这里不止调用

        add

        会报错,调用

        ArrayList

        本身的

        remove

        也会报错,

        关键在于这些方法修改了

        modCount

        但是没有修改

        expectedModCount



      • 如果我们是用迭代器的

        remove

        方法则不会报错,因为迭代器的remove方法在删除元素后会将新的

        modCount

        的值赋给

        expectedModCount


        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
        
            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;//这个cursor表示的是下一个需要遍历的元素,lastRet表示上一次遍历到的元素
                lastRet = -1;
                expectedModCount = modCount;//这里会保持两者是一致的
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
        

        • 注意:我们在删除

          lastRet

          索引位置表示的上一个遍历的元素后,将

          lastRet

          的索引赋给

          cursor

          表示下一个需要遍历的索引位置


          • 这个时候可能会有疑惑:为什么这个位置元素被删除了下一次遍历还是这个位置


            • 这是因为我们

              remove

              删除上一个元素后,数组大小-1,即被删除元素后的元素都会往前移动,此时lastRet这个索引在新数组中指向的就是刚刚的下一个元素


        • 删除之后将

          lastRet

          置为-1,而remove方法开始就会判断

          lastRet

          是否小于0,防止连续两次调用

          remove

          方法删除元素

    • ArrayList中的iterator私有内部类是这样定义的:

      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;
      
          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];
          }
      
          public void remove() {
              if (lastRet < 0)
                  throw new IllegalStateException();
              checkForComodification();
      
              try {
                  ArrayList.this.remove(lastRet);
                  cursor = lastRet;
                  lastRet = -1;
                  expectedModCount = modCount;
              } catch (IndexOutOfBoundsException ex) {
                  throw new ConcurrentModificationException();
              }
          }
      
          @Override
          @SuppressWarnings("unchecked")
          public void forEachRemaining(Consumer<? super E> consumer) {
              Objects.requireNonNull(consumer);
              final int size = ArrayList.this.size;
              int i = cursor;
              if (i >= size) {
                  return;
              }
              final Object[] elementData = ArrayList.this.elementData;
              if (i >= elementData.length) {
                  throw new ConcurrentModificationException();
              }
              while (i != size && modCount == expectedModCount) {
                  consumer.accept((E) elementData[i++]);
              }
              // update once at end of iteration to reduce heap write traffic
              cursor = i;
              lastRet = i - 1;
              checkForComodification();
          }
      
          final void checkForComodification() {
              if (modCount != expectedModCount)
                  throw new ConcurrentModificationException();
          }
      }
      



版权声明:本文为w55935原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。