集合---list

   Iterable 是所有集合的超类, collection继承了iterable ,而list和set又继承了 collection,而在Iterable中定义了一个iterator()方法返回一个迭代器Iterator

而 Iterator是一个超类接口,为各种容器提供了公共的操作接口,里边有 hasNext(),next(),remove()三个方法

    有子接口ListIterator和XMLEventReader

  在ArrayList中的使用  

     List<String> list=new ArrayList<>();
          list.add("abc");
          list.add("edf");
          list.add("ghi");
          for(Iterator<String> it=list.iterator();it.hasNext();)
          {
              System.out.println(it.next());
          }

    在ArrayList内部定义了一个内部类Itr,该类实现了Iterator接口。

      在Itr中,有三个变量分别是,cursor:表示下一个元素的索引位置,lastRet:表示上一个元素的索引位置,expectModCount:预期被修改的次数

        

  关于遍历是不就可以删除集合中元素的问题:

    如果在上边的for循环中增加list.remove(“abc”),会出现ConcurrentModificationException异常。

    因为在你迭代之前,迭代器已经被通过list.itertor()创建出来了,如果在迭代的过程中,又对list进行了改变其容器大小的操作,那么Java就会给出异常。

      因为此时Iterator对象已经无法主动同步list做出的改变,Java会认为你做出这样的操作是线程不安全的,就会给出善意的提醒(抛出                                    ConcurrentModificationException异常)

      ArrayList中的源码实现        

        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;    //当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();
          }
      }

  通过查看源码发现原来检查并抛出异常的是checkForComodification()方法。在ArrayList中modCount是当前集合的版本号,  

    每次修改(增、删)集合都会加1;expectedModCount是当前迭代器的版本号,在迭代器实例化时初始化为modCount。我们看到在                                                 checkForComodification()方法中就是在验证modCount的值和expectedModCount的值是否相等,

    所以当你在调用了ArrayList.add()ArrayList.remove()时,只更新了modCount的状态,而迭代器中的expectedModCount未同步,

       因此才会导致再次调用Iterator.next()方法时抛出异常。

  但是为什么使用Iterator.remove()就没有问题呢?通过源码高亮的行发现,在Iterator的remove()中同步了expectedModCount的值,所以当你下次再调用next()的时候,检查不会抛出异常。

  

for循环与迭代器的对比:

  * 效率上各有各的优势:

    ArrayList对随机访问比较快,而for循环中使用的get()方法,采用的即是随机访问的方法,因此在ArrayList里for循环快。

    LinkedList则是顺序访问比较快,Iterator中的next()方法采用的是顺序访问方法,因此在LinkedList里使用Iterator较快。

    主要还是要依据集合的数据结构不同的判断。

 

猜你喜欢

转载自www.cnblogs.com/xp0813/p/11074104.html