我是靠谱客的博主 自由大神,最近开发中收集的这篇文章主要介绍20:Iterator、iterator、Iterable、ListIterator、listIterator的使用及区别及源码中它们的具体实现:Iterator、iterator、Iterable、ListIterator、listIterator的区别:iterator 、listIterator 方法的基本使用和底层实现解读:ArrayList集合的 iterator 方法的底层实现源码解读:LinkedList 继承自 AbstractList 的 iterator 方法 源码解读:Ar,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

Iterator、iterator、Iterable、ListIterator、listIterator的区别:

Iterator接口和它的方法:​

Iterator、iterator、Iterable、ListIterator、listIterator它们之间的关系:

Iterator、iterator、Iterable、ListIterator、listIterator的区别总结:

iterator 、listIterator 方法的基本使用和底层实现解读:

ArrayList重写后的iterator的基本使用:

ArrayList集合的 iterator 方法的底层实现源码解读:

LinkedList 的继承自AbstractList 抽象父类的 iterator 方法的基本使用:

LinkedList 继承自 AbstractList 的 iterator 方法 源码解读:

ArrayList、LikedList 的 listIterator 迭代器的基本使用。

ArrayList 集合的 listIterator 方法的具体实现源码解读:


Iterator、iterator、Iterable、ListIterator、listIterator的区别:

  • Iterable接口、和它的 iterator方法:

  • Iterator接口和它的方法:

  • ListIterator接口和它的方法:

  • ArrayList、LinkedList 集合中的 listIterator方法:

  • ArrayList和LinkedList的 iterator 方法:(LinkedList的iterator方法是继承自AbstringList抽象父类,而ArrayList重写了该方法)

  • ArrayList和LinkedList的共同抽象父类定义的listIterator方法:

  • Iterator、iterator、Iterable、ListIterator、listIterator它们之间的关系:

  • Iterable:
  • iterator方法:
  • Iterator接口和ListIterator接口:

     

  • Iterator、iterator、Iterable、ListIterator、listIterator的区别总结:

    • Iterable 是ArrayList和LinkedList的共同父接口。
    • 而 iterator 是Iterable 接口中的 一个抽象方法:Iterator 是 Iterable 接口的 iterator 抽象方法的返回值。 由 AbstractList抽象父类进行了实现了 iterator方法,AbstractList的 Itr 内部类实现了 Iterator接口。只有ArrayList进行了重写,ArrayList中 Itr 内部类重写实现了 Iterator接口。
    • ListIterator 是 Iterator 接口的子接口。listIterator 方法是 集合们的抽象父类 AbstractList中的方法,由它里面的 ListItr 内部类实现了 ListIterator 接口。 ArrayList重写了有参和无参的 listIterator 方法,并且ArrayList 中的 ListItr 内部类 继承了 Itr 内部类并重写实现了 ListIterator 接口 ,LinkedList 只重写了 有参的 listIterator 方法,并且 LinkedList 中的 ListItr 内部类重新实现了ListIterator接口。

iterator 、listIterator 方法的基本使用和底层实现解读:

  • ArrayList重写后的iterator的基本使用:

     public static void arrayListUseIterator() {
            List<String> list = new ArrayList<>();
            list.add("后端开发");
            list.add("前端开发");
            list.add("测试");
            list.add("运维");
            list.add("市场");
            list.add("项目经理");
            list.add("HR");
            list.remove(2);
    
            // 获取一个 Iterator 迭代器遍历集合
            // Iterator 迭代器只能使用一次。
            // 如果还想用 Iterator迭代器遍历,则需要重写获取
            Iterator<String> iterator = list.iterator();
    //        // 集合中是否有下一个元素
    //        while (iterator.hasNext()) {
    //            // 拿到当前元素
    //            String next = iterator.next();
    //            System.out.println(next);
    //        }
    
            /**
             *
             */
            while (iterator.hasNext()) {
                System.out.println(iterator.next());
                // remove方法只能用在 next 的后面,否则抛出
                // IllegalStateException(非法状态 )异常
                // 取出一个元素,删除一个元素,遍历结束,
                // 集合也就为 空
                // 如果一边 remove 一边修改,
                // 则抛出 IndexOutOfBoundsException
                // iterator.remove();
    
                // 如果在遍历的时候,又往集合添加元素或者 删除某个元素
                // 或者 一边调用remove方法删除元素、一边 往集合中添加元素
                // 则抛出 ConcurrentModificationException (并发修改异常)
                // 因为有两个引用在操作同一个数组
                // 因为底层 有个 modCount属性记录着操作集合的次数!
                // 而迭代器内部 有个 expectedModCount,它的值由 modCount 初始化
                // expectedModCount 属性记录着当前迭代器操作的次数 
                // 这个才开始遍历的时候和modCount相同
                // 每遍历一个元素,expectedMouCount都会++!
                /* 集合的get操作和set操作, modCount没有记录,
                所以不会引发ConcurrentModificationException */
    //            list.add("小组长");
            }
            for (String s : list) {
                System.out.print(s + "t");
            }
            System.out.println();
            System.out.println("list = " + list);
        }

    result:

  • 其实集合的 增强 for 循环也是通过 迭代器来完成的!,step 1 动态的获取迭代器:step 2:调用迭代器的 hashNext 方法:step 3:调用迭代器的 next 方法,获取元素:注: 而数组的 增强for 循环不是通过迭代器来完成的,数组没有迭代器。数组的增强 for 循环是编译器编译好字节码文件后,由java命令启动 JVM 运行程序时来完成的!

  • 注:可以通过 迭代器的 forEachRemaning方法 遍历。此方法和 forEach 的使用一样,一样的可以使用 匿名内部类使用 Comsumer接口、 Lambda 表达式、方法引用!!,result:


  • ArrayList集合的 iterator 方法的底层实现源码解读:

  • step 1:
  • step 2:源码解读:
    private class Itr implements Iterator<E> {
        /**
         * cursor : 光标、指针,
         * 由cursor变量模拟一个指针,初始化值 0
         */
        int cursor;       // index of next element to return
        /**
         * 返回的最后一个元素的索引
         */
        int lastRet = -1; // index of last element returned; -1 if no such
        /**
         * expectedModCount:预期的操作数,
         * 由 modCount 的值 赋值初始化,
         * 这个modCount在 AbstractList 抽象父类中,
         * ArrayList继承自AbstractList 可以直接使用
         * 它记录的就是当前集合的 增、删 操作的 次数。
         */
        int expectedModCount = modCount;
    
        /**
         * 无参构造器
         */
        Itr() {}
    
        /**
         * 是否有下一个元素
         * @return 比较结果
         */
        public boolean hasNext() {
            /**
             * 如果 指针 不等于 集合的长度。
             * 也就是ArrayList底层数组的实际长度
             */
            return cursor != size;
        }
    
        /**
         * 获取下一个元素
         * @return 当前遍历的元素
         */
        @SuppressWarnings("unchecked")
        public E next() {
            // 检查 当前迭代器的操作的次数。
            checkForComodification();
            // i 初始值 为 cursor,第一次为 0
            int i = cursor;
            // 如果 i 大于等于 当前集合的长度。一般遍历完再调用迭代器的 next 方法就会抛出
            // 抛出 没有这样的元素 异常
            if (i >= size)
                throw new NoSuchElementException();
                /* 把ArrayList的elementData数组 赋值给
                当前Itr 内部类的 elementData 让它指向。*/
            Object[] elementData = ArrayList.this.elementData;
            // 如果 i 大于等于 数组的长度。也就是 和 size 是一样的值。
            // 则抛出 并发修改 异常
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            // 指针等于 i + 1
            cursor = i + 1;
            // 先把 i 的值赋值为 lastRet,第一次i的值为0,
            // 再返回数组中的元素
            return (E) elementData[lastRet = i];
        }
    
        /**
         * 删除元素
         */
        public void remove() {
            // 如果lastRet小于 0,
            // 则抛出 非法状态异常
            // 这也是为什么要先 next 再remove 的原因!
            // 底层代码已经写死了的!
            if (lastRet < 0)
                throw new IllegalStateException();
            // 检查当前迭代器操作数组的次数!
            checkForComodification();
    
            try {
                // 调用 ArrayList 的remove方法删除元素
                // 第一次 lastRet 由 i 赋值为 0
                ArrayList.this.remove(lastRet);
                // 指针的 值 为 lastRet,第一次为 0
                // 如果 一直remove 则,lastRet的值一直都是为 0
                cursor = lastRet;
                // lastRet 的值 Remove 一次就置为 -1,
                // 从而和 next 方法形成了闭环
                lastRet = -1;
                // expectedModCount:预期的操作数,又置为 modCount
                // 需要清楚的是,每次 迭代器的remove 方法
                // 调用ArrayList 的remove方法时。
                // 在ArrayList中的 modCount 记录的增、删操作的次数都会 ++
                // 迭代器中的remove方法让 expectedModCount
                // 保持了 和 modCount 同步的操作,索引不会造成 并发修改异常!
                expectedModCount = modCount;
                // 即如果使用迭代器遍历操作完之后,又在迭代器的外部使用了 remove 方法
              // 的话则 捕获索引越界异常 改抛 并发修改 异常!
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    
        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            // 如果 调用方法传入进来的 consumer 对象为null
            // 则抛出 空指针 异常
            Objects.requireNonNull(consumer);
            // 获取当前集合的大小
            final int size = ArrayList.this.size;
            // 初始i等于 cursor ,第一次 为 0
            int i = cursor;
            // 如果 i 大于等于集合大小,结束方法
            if (i >= size) {
                return;
            }
            // 获取当前集合的 elementData数组让 当前迭代器的 elementData数组指向。
            final Object[] elementData = ArrayList.this.elementData;
    
            // 如果 i 大于等于 数组长度 则抛出 并发修改异常
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            // 进入 while 循环,条件是 i(cursor) 不等于 集合大小 且
            // 集合操作的增、删的次数始终都 等于 当前迭代器操作的次数
            while (i != size && modCount == expectedModCount) {
                // 调用 Consumer 接口的实现类的
                // accept (接收)方法,返回当前 数组中的对应索引的
                // 元素给 实现类使用!
                // 然后 i再 ++
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
    
            // cursor 等于当前 的 i。
            cursor = i;
            // lastRet 等于当前的  i - 1
            lastRet = i - 1;
            // 遍历完之后,再检查是否 并发修改!如果在使用 forEachRemaining
            // 遍历时有对集合中元素增、删的操作时,则抛出并发修改异常!
            checkForComodification();
        }
    
        final void checkForComodification() {
            // 出现这个异常的情况一般都是 一边删除集合中的元素,一边又往集合中添加元素
            // 或者一边调用 迭代器的 remove方法,
            // 一边又调用 ArrayList 集合中增、删 的方法!
            // 因为 modCount 和 expectedModCount 不是迭代器一个
            // "人" 在操作,没有进行同步,所以抛出并发修改异常!
            // 则抛出 并发修改 异常
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
    

     

  • LinkedList 的继承自AbstractList 抽象父类的 iterator 方法的基本使用:

    public static void linkedListUseIterator(){
            LinkedList<String> linkedList = new LinkedList<>();
            linkedList.add("李世民");
            linkedList.add("李元霸");
            linkedList.add("李治");
            linkedList.add("武则天");
            linkedList.add("程咬金");
            linkedList.add("秦叔宝");
            linkedList.add("尉迟恭");
    
            Iterator<String> iterator = linkedList.iterator();
    
            // 遍历
            while (iterator.hasNext()) {
                System.out.println("iterator.next() = " + iterator.next());
            }
    
            String[] strArr = new String[linkedList.size()];
            linkedList.iterator().forEachRemaining(new Consumer<String>() {
                private int count;
                @Override
                public void accept(String s) {
                    strArr[count] = s;
                    count++;
                }
            });
            System.out.println("Arrays.toString(strArr) = " + Arrays.toString(strArr));
        }

    result:

  • LinkedList 继承自 AbstractList 的 iterator 方法 源码解读:

  1. step 1:

  2. step 2:进入到 AbstractSequentialList 抽象父类中,惊奇的发现 iterator 做了一件调用listIterator 方法的动作,而且还把返回值类型 限制为了 Iterator接口!(妙!)

  3. step 3:进入到 AbstractList 抽象父类中 (如果运行时类型有重写 listIterator的话,则多态动态机制的绑定调用实现类重写的方法!如果没有则调用父类中的方法!)

  4. 多态的动态绑定机制又回到 LinkedList 集合中,因为 LinkedList 集合重写了 listIterator方法:

  5. step 5:LinkedList中的 ListIterator 迭代器具体实现源码解读:

        private class ListItr implements ListIterator<E> {
            // 声明当前返回节点,初始化值 为 null
            private LinkedList.Node<E> lastReturned;
            // 声明下一个节点,初始化值为 null
            private LinkedList.Node<E> next;
            // 声明下一个索引,初始化值为 0
            private int nextIndex;
            // 当前迭代器的操作次数,由modCount记录的LinkedList 集合的
            // 增、删操作次数 赋值 初始化
            private int expectedModCount = modCount;
    
            // 有参构造器,如果是调用的 LinkedList的 listIterator
            // 方法获取列表迭代器传入 index 为0 和
            // 调用 LinkedList 继承下来的 iterator 方法 的结果是一样的!!!!
            ListItr(int index) {
                // assert isPositionIndex(index);
                // 如果index和当前链表的长度相等则返回 null
                // 不相等 则获取 对应的索引的节点,赋值给 next 下一个节点
                // (next节点指向就是当前元素的那个节点)
                next = (index == size) ? null : node(index);
                //  当前节点的索引 赋值给下一个节点的索引 !
                nextIndex = index;
            }
    
            /**
             * @return 如果当前遍历的节点的索引
              小于当前链表的长度。则为true
             */
            public boolean hasNext() {
                return nextIndex < size;
            }
    
            /**
             * 获取下一个节点的元素
             * @return
             */
            public E next() {
                // 健壮性考虑,如果在使用迭代器的时候
                // 由 同时删除元素、或者增加元素,则抛出 并发修改 异常
                checkForComodification();
                // 如果没有下一个元素,说明已经遍历完了
                // 如果在遍历完之后还要再调用 迭代器 next 方法
                // 。则抛出 没有这样的元素 异常
                if (!hasNext())
                    throw new NoSuchElementException();
    
                // next:表示为当前遍历的元素的节点
                // lastReturned:表示为最后返回的节点,
                // 这里next赋值给了 lastReturned,所以它们指向的同一个节点
                lastReturned = next;
                // 这里的 next: 等于 当前遍历的元素的节点的下一个节点(也就是这一次遍历完,
                // 下一次再进入next方法遍历的元素的节点)
                next = next.next;
                // 下一个节点的是当前遍历的元素的节点的索引++
                nextIndex++;
                // 返回 (当前遍历的节点)存储的元素
                return lastReturned.item;
            }
    
            /**
             *
             * @return 是否有上一个节点
             (逆向遍历,逆向遍历一定要在正向遍历完之后才能进行,
             不然nextIndex的值为 0,条件不成立,也就无法遍历! )
             */
            public boolean hasPrevious() {
                return nextIndex > 0;
            }
    
            /**
             *
             * @return 获取上一个节点的元素
    //         */
    //        public E previous() {
                // 健壮性考虑,如果在使用迭代器的时候
                // 又同时删除元素、或者增加元素,则抛出 并发修改 异常
                checkForComodification();
                // 如果没有下一个元素,说明已经逆向遍历到首节点了。
                // 首节点的索引为0 , 则抛出 没有这样的元素 异常
                if (!hasPrevious())
                    throw new NoSuchElementException();
    
                // 如果这个条件成立,则这个几点是尾节点 (next == null)
                // 则返回尾节点,给next(当前正在遍历的节点)然后再给 lastReturned
                // (返回节点的元素的节点!),
                // 即next 和 lastReturned 的值是一样的!
                // 不为空则返回上一次遍历过的节点(这里由 next变量保存)的上一个节点 ,
                // 给next然后再给lastReturned
                // 即next 和 lastReturned 的值是一样的!
                lastReturned = next = (next == null) ? last : next.prev;
                // 当前遍历的节点的索引 --,这里的 nextIndex-- 的操作,
                // 正是正在遍历的节点的索引!如果是正向遍历完之后,
                // 又逆向遍历进来,这个 nextIndex 的值是 和链表的长度一致!
                nextIndex--;
                // 返回当前节点的元素
                return lastReturned.item;
            }
    
            /**
             *
             * @return 返回下一个节点的索引
             */
            public int nextIndex() {
                return nextIndex;
            }
    
            /**
             *
             * @return 返回上一个节点的索引
             */
            public int previousIndex() {
                return nextIndex - 1;
            }
    
            /**
             * 删除节点元素
             */
            public void remove() {
                // 健壮性考虑,如果在使用迭代器的时候
                // 又同时删除元素、或者增加元素,则抛出 并发修改 异常
                checkForComodification();
                // 如果当前节点为null ,抛出 非法状态异常
                // 出现这个异常的情况就是遍历完之后,已经在迭代器中删除了元素。
               // 让后又在迭代器外  调用 remove 方法,就会触发
                 //(因为正向遍历,每次索引都会++。逆向遍历,每次索引都会--)
                if (lastReturned == null)
                    throw new IllegalStateException();
    
                // 取出 当前正在遍历的节点的下一个节点
                LinkedList.Node<E> lastNext = lastReturned.next;
                // 调用 unlink 方法 删除当前正在遍历的 节点
                unlink(lastReturned);
    
                // 如果当前节点的下一个节点和当前节点相同,
                // 则说明是逆向遍历删除,且remove方法是用在previous方法的后面,
                // 才会有这个结果!如果是逆向遍历删除,这里的next都指向lastNext
                // 这里的 next 都是尾节点(即为 null )
               // 只有这样! lastReturned = next = (next == null) ? last : next.prev;
               // 条件才会满足!
                if (next == lastReturned)
                         下一个节点指向当前节点的下一个节点
                    next = lastNext;
                else
                 // 如果是不是逆向遍历,正向遍历,则 下一个节点(当前节点)的索引 --
                    // 即把当前删除的元素的节点索引,
                  // 下一次进入到remove方法删除的节点的索引正好和上一次进入到remove方法删除的节点的索引相同!
                    nextIndex--;
                 // 最后返回的节点(也就是当前遍历到的这个节点)置为 null,
                   //  unlink(lastReturned) 方法已经把当前节点指向的上一个节点
                   // 和下一个节点和当前索引对应的节点的元素置为了null,
                  // 这里把当前这个遍历的节点删除后节点本身的地址置为null
                lastReturned = null;
             // 增、删 操作次数++
                expectedModCount++;
            }
    
              //设置当前遍历的节点的元素
             // 此方法不能使用在remove方法的后面
             // ,因为remove方法在删除完之后lastReturned置为了null
            // 也不能使用在 next 方法的前面!lastReturned的初始化值就为null!
            public void set(E e) {
                     //健壮性考虑,如果当前遍历的节点为null
                     // (一般都是删除后才会把lastReturned置为null)
                if (lastReturned == null)
                    throw new IllegalStateException();
    
                // 健壮性考虑,如果在使用迭代器的时候
                 // 如果又是修改、又是删除
                // 又同时删除元素、或者增加元素,则抛出 并发修改 异常
                checkForComodification();
                // 修改当前返回的索引的元素,为用户传入的值
                lastReturned.item = e;
            }
    
              // 添加元素,此方法不能使用在 remove 方法的前面!
              // 因为 lastReturn 依然是为null的!
            public void add(E e) {
                     // 健壮性考虑,如果在使用迭代器的时候
    //            // 又同时删除元素、或者增加元素,则抛出 并发修改 异常
                checkForComodification();
                    // 当前最后返回的元素为 null,说明是在删除后置为的null
                lastReturned = null;
                     // 如果当前节点为null,说明调用这个方法的时候是逆向遍历,且add方法在next 方法的前面!
                     // 则添加元素到链表的末尾
                if (next == null)
                    linkLast(e);
                else
    //             //  如果不是 则添加到 当前元素的前面并且和当前元素形成双向链表的关系,
                  //(换言之就是当前添加的元素取代了当前正在遍历的这个元素)
                   // (当前这个遍历的这个元素的节点的索引就得往后挪)
                    linkBefore(e, next);
                //下一个节点的是当前遍历的节点的索引++,因为链表的长度已经++了,
                // 所以 nextIndex++这个操作正是下一次遍历的元素的节点索引变化的操作!
                nextIndex++;
                 //操作次数++,和modCount记录的次数保持一致,否则抛出 并发修改异常!
                expectedModCount++;
            }
    
            public void forEachRemaining(Consumer<? super E> action) {
                 // 如果传入进来的对象 为 null ,抛出空指针异常
                Objects.requireNonNull(action);
    
                 // 条件是:
                 // 集合的增、删的操作次数要和当前迭代器的操作次数一样,
                 // 并且 当前节点的索引小于链表长度
                while (modCount == expectedModCount && nextIndex < size) {
                         //调用 实现类的 accept 方法啊,传入 节点元素给实例类
                    action.accept(next.item);
                        // 把当前元素的节点 赋值给 lastReturned(最后返回的节点)
                        即 lastReturned 和next指向的地址是是一样的
                    lastReturned = next;
                         // 下一次遍历的节点是当前节点的下一个节点。
                        //注:这里的 next 指向的地址已经发生了变化!!!
                    next = next.next;
                        // 下一次遍历的节点的索引是当前节点的索引++。
                    nextIndex++;
                }
                     // 检查是否在forEachRemaining的时候由对集合
                     // 进行 增、删的并发操作。(指的是,单独调用集合的增、删API时就会产生!
                     // modCount != expectedModCount,因为没有调用迭代器的API没有同步操作 )
                     // 如果是则抛出 并发操作异常!
                checkForComodification();
            }
    
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }
    }
  • 说明: LinkedList 的 iterator 获取迭代器的方法 和 直接调用 listIterator 方法 传入索引 0 的获取 列表迭代器 效果是一样的,即调用 iterator方法时实际 就和 直接调用 listIterator 方法传入索引 0 返回的迭代器一样的!LinkedList 只有 listIterator  这一个方法的实现。只有说 在接收对象的编译时 数据类型 如果是 Iterator 接口的话,那么就只能 调用 实现类实现的 Iterator 接口中的方法! 如果是用 调用listIterator方法 获取列表迭代器的用 ListIterator 接口的编译时 数据类型接收的 话,那么就可以调用 实现类实现的 ListIterator 接口中的所有的方法!

  • 如果是传入了大于 0 且 小于链表长度的索引的 话,则取的节点就是 传入的索引的节点!如果调用listIterator方法时 是传入索引为 0 就和直接调用 iterator 方法没有什么区别。如果是传入的刚好是链表的长度的话,那么在 调用 hasNext 方法时就不成立!就不会进行遍历了!(底层源码写得很巧妙!)


     

  • ArrayList、LikedList 的 listIterator 迭代器的基本使用。

    public static void listIterator() {
    
            List<String> arrayList = new ArrayList<>();
            arrayList.add("键盘");
            arrayList.add("鼠标");
            arrayList.add("显卡");
            arrayList.add("CPU");
            arrayList.add("总线");
            arrayList.add("USB");
            arrayList.add("网卡");
    
            listUseListIterator(arrayList);
            System.out.println("===============================================");
            listUseListIterator(new LinkedList<>(arrayList));
        }
    
        public static void listUseListIterator(List<? super String> list) {
            // 如果是使用 Iterator 迭代器的时候,
            // 如果想一边遍历,一边调用ArrayList、LinkedList 集合的增、删的API的话
            // 就会抛出 ConcurrentModificationException!!
            Iterator<? super String> iterator = list.iterator();
    //        while (iterator.hasNext()) {
    //            // 如果下一个元素为 "CPU" 就在它的后面添加 内存条 词条
    //            // 再把 "网卡" 给删除掉
    //            /* 抛出: java.util.ConcurrentModificationException 并发修改异常!*/
    //            if ("CPU".equals(iterator.next())){
    //                arrayList.add("内存条");
    //                arrayList.remove("网卡");
    //            }
    //        }
            // 获取 ListIterator 迭代器
            ListIterator<? super String> listIterator = list.listIterator();
            // 而使用 ListIterator 迭代器就可以
            // 一边遍历、一边增加、或删除 集合中的元素
            // 前提是 必须使用 ListItr (ArrayList的内部类)
            // 实现自 ListIterator 接口的API,
            // 如果是使用的 ArrayList 、LinkedList 的API的话,
            // 那么操作次数不同步,一样的会抛出
            // ConcurrentModificationException!!
            while (listIterator.hasNext()) {
                String next = (String) listIterator.next();
                // 如果下一个元素为 "CPU" 就在它的后面添加 内存条 词条
                if ("CPU".equals(next)) {
                    listIterator.add("内存条");
                }
                // 如果 下一个元素 为 "鼠标" 的话,就把 它修改为 蓝牙
                if ("鼠标".equals(next)) {
                    listIterator.set("蓝牙");
                }
                System.out.println("next = " + next);
            }
            System.out.println(list);
    
            // 还可以 逆向遍历(必须在正向 遍历完之后才可以!)
            while (listIterator.hasPrevious()) {
                String previous = (String) listIterator.previous();
                // 如果 上一个 元素是 内存条 的话就修改为 机械键盘
                if ("内存条".equals(previous)) {
                    listIterator.set("机械键盘");
                }
                System.out.println("previous = " + previous);
            }
            System.out.println(list);
        }

    result:


     

  • ArrayList 集合的 listIterator 方法的具体实现源码解读:

  • 说明:LinkedList 的 iterator 和 listIterator 方法本质上是一样的,底层都是只实现了 listIterator 方法。!
  • 跟进:
  • step 2:
  • 源码解读:
    private class Itr implements Iterator<E> {
        /**
         * cursor : 光标、指针,
         * 由cursor变量模拟一个指针,初始化值 0
         */
        int cursor;       // index of next element to return
        /**
         * 返回的最后一个元素的索引
         */
        int lastRet = -1; // index of last element returned; -1 if no such
        /**
         * expectedModCount:预期的操作数,
         * 由 modCount 的值 赋值初始化,
         * 这个modCount在 AbstractList 抽象父类中,
         * ArrayList继承自AbstractList 可以直接使用
         * 它记录的就是当前集合的 增、删 操作的 次数。
         */
        int expectedModCount = modCount;
    
        /**
         * 无参构造器
         */
        Itr() {}
    
        /**
         * 是否有下一个元素
         * @return 比较结果
         */
        public boolean hasNext() {
            /**
             * 如果 指针 不等于 集合的长度。
             * 也就是ArrayList底层数组的实际长度
             */
            return cursor != size;
        }
    
        /**
         * 获取下一个元素
         * @return 当前遍历的元素
         */
        @SuppressWarnings("unchecked")
        public E next() {
            // 检查 当前迭代器的操作的次数。
            checkForComodification();
            // i 初始值 为 cursor,第一次为 0
            int i = cursor;
            // 如果 i 大于等于 当前集合的长度。一般遍历完再调用迭代器的 next 方法就会抛出
            // 抛出 没有这样的元素 异常
            if (i >= size)
                throw new NoSuchElementException();
                /* 把ArrayList的elementData数组 赋值给
                当前Itr 内部类的 elementData 让它指向。*/
            Object[] elementData = ArrayList.this.elementData;
            // 如果 i 大于等于 数组的长度。也就是 和 size 是一样的值。
            // 则抛出 并发修改 异常
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            // 指针等于 i + 1
            cursor = i + 1;
            // 先把 i 的值赋值为 lastRet,第一次i的值为0,
            // 再返回数组中的元素
            return (E) elementData[lastRet = i];
        }
    
        /**
         * 删除元素
         */
        public void remove() {
            // 如果lastRet小于 0,
            // 则抛出 非法状态异常
            // 这也是为什么要先 next 再remove 的原因!
            // 底层代码已经写死了的!
            if (lastRet < 0)
                throw new IllegalStateException();
            // 检查当前迭代器操作数组的次数!
            checkForComodification();
    
            try {
                // 调用 ArrayList 的remove方法删除元素
                // 第一次 lastRet 由 i 赋值为 0
                ArrayList.this.remove(lastRet);
                // 指针的 值 为 lastRet,第一次为 0
                // 如果 一直remove 则,lastRet的值一直都是为 0
                cursor = lastRet;
                // lastRet 的值 Remove 一次就置为 -1,
                // 从而和 next 方法形成了闭环
                lastRet = -1;
                // expectedModCount:预期的操作数,又置为 modCount
                // 需要清楚的是,每次 迭代器的remove 方法
                // 调用ArrayList 的remove方法时。
                // 在ArrayList中的 modCount 记录的增、删操作的次数都会 ++
                // 迭代器中的remove方法让 expectedModCount
                // 保持了 和 modCount 同步的操作,索引不会造成 并发修改异常!
                expectedModCount = modCount;
                // 即如果使用迭代器遍历操作完之后,又在迭代器的外部使用了 remove 方法
              // 的话则 捕获索引越界异常 改抛 并发修改 异常!
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    
        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            // 如果 调用方法传入进来的 consumer 对象为null
            // 则抛出 空指针 异常
            Objects.requireNonNull(consumer);
            // 获取当前集合的大小
            final int size = ArrayList.this.size;
            // 初始i等于 cursor ,第一次 为 0
            int i = cursor;
            // 如果 i 大于等于集合大小,结束方法
            if (i >= size) {
                return;
            }
            // 获取当前集合的 elementData数组让 当前迭代器的 elementData数组指向。
            final Object[] elementData = ArrayList.this.elementData;
    
            // 如果 i 大于等于 数组长度 则抛出 并发修改异常
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            // 进入 while 循环,条件是 i(cursor) 不等于 集合大小 且
            // 集合操作的增、删的次数始终都 等于 当前迭代器操作的次数
            while (i != size && modCount == expectedModCount) {
                // 调用 Consumer 接口的实现类的
                // accept (接收)方法,返回当前 数组中的对应索引的
                // 元素给 实现类使用!
                // 然后 i再 ++
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
    
            // cursor 等于当前 的 i。
            cursor = i;
            // lastRet 等于当前的  i - 1
            lastRet = i - 1;
            // 遍历完之后,再检查是否 并发修改!如果在使用 forEachRemaining
            // 遍历时有对集合中元素增、删的操作时,则抛出并发修改异常!
            checkForComodification();
        }
    
        final void checkForComodification() {
            // 出现这个异常的情况一般都是 一边删除集合中的元素,一边又往集合中添加元素
            // 或者一边调用 迭代器的 remove方法,
            // 一边又调用 ArrayList 集合中增、删 的方法!
            // 因为 modCount 和 expectedModCount 不是迭代器一个
            // "人" 在操作,没有进行同步,所以抛出并发修改异常!
            // 则抛出 并发修改 异常
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
    
    
    // ListItr 继承自 Itr(Iterator 迭代器实现类,所以可以直接使用Itr中实现的方法!)
    // 实现自 ListIterator(列表迭代器)(ListIterator是Iterator的子接口!)
    private class ListItr extends Itr implements ListIterator<E> {
        // 有参构造器,
        // 用户传入的索引,
        // 赋值给 cursor 属性。(这个属性继承自 Itr 父类)
        // 如果用户没有传入索引
        // cursor 默认初始化值为 0
        ListItr(int index) {
            super();
            cursor = index;
        }
    
        /**
         *
         * @return 是否有上一个元素!
         * 此方法可以用于逆向遍历!
         * 此方法只有在正向遍历完之后,才可以逆向遍历!
         */
        public boolean hasPrevious() {
            return cursor != 0;
        }
    
        /**
         *
         * @return 返回下一个元素的索引
         */
        public int nextIndex() {
            return cursor;
        }
    
        /**
         *
         * @return 返回上一个元素的索引
         */
        public int previousIndex() {
            return cursor - 1;
        }
    
        // 返回上一个索引的元素
        @SuppressWarnings("unchecked")
        public E previous() {
            // 健壮性考虑,如果用户调用了ArrayList的
            // 增、删的API对集合进行操作的话,则抛出并发修改异常!
            checkForComodification();
            // i 的 cursor - 1 (正向遍历完之后又逆向遍历的话,
            // 那么cursor在正向遍历完之后最后的值是数组的长度!)
            int i = cursor - 1;
            // 健壮性考虑,如果用户在迭代器逆向遍历完之后
            // 还要在 迭代器外部使用的话则抛出 异常
            if (i < 0)
                throw new NoSuchElementException();
    
            // previous方法中的 elementDate 数组 指向ArrayList中的数组!
            Object[] elementData = ArrayList.this.elementData;
    
            // 健壮性考虑,即在遍历的时候使用了 ArrayList 的增、删的API
            // 对集合中的元素进行操作,就会抛出 并发修改 异常!
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
             //cursor 等于当前 遍历索引,即就是下一个元素在数组中的长度!
            cursor = i;
             //  先把 i的值 赋值给 lastRet ,再返回索引对应的元素!
            return (E) elementData[lastRet = i];
        }
    
          // 设置元素
        public void set(E e) {
                 //如果用户在 previous方法之前设置
      //  元素则抛出非法状态异常! 元素都没有获取到,无法修改
            if (lastRet < 0)
                throw new IllegalStateException();
                // 如果在使用迭代器中的set 方法之前,使用了
     //      ArrayList集合的 增、删API的话,则抛出 并发修改异常!
            checkForComodification();
    
            try {
                     // 底层依然是 调用了ArrayList的set方法
                ArrayList.this.set(lastRet, e);
                // 即如果使用迭代器遍历操作完之后,又在迭代器的外部使用了 迭代器的 set 方法
              // 的话则 捕获索引越界异常 改抛 并发修改 异常!
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    
            // 添加元素
        public void add(E e) {
          // 如果在使用迭代器中的add 方法之前,使用了
        //   ArrayList集合的 增、删API的话,则抛出 并发修改异常!
            checkForComodification();
    
            try {
               // i = 当前的 cursor ,即当前元素的索引!当前元素就往后挪
                int i = cursor;
                  // 迪奥ArrayList方法 将元素添加到指定索引的位置
                ArrayList.this.add(i, e);
                 cursor 等于 i + 1,即下一个元素的索引就是当前添加的元素的索引+1
                cursor = i + 1;
                 lastRet 置为 -1
                lastRet = -1;
                    这里的 expectedModCount = modCount 同步了操作集合的次数,所以不会 抛出 并发修改异常!
                expectedModCount = modCount;
    
                 // 即如果使用迭代器遍历操作完之后,又在迭代器的外部使用了迭代器的 add 方法
              // 的话则 捕获索引越界异常 改抛 并发修改 异常!
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    }

     

最后

以上就是自由大神为你收集整理的20:Iterator、iterator、Iterable、ListIterator、listIterator的使用及区别及源码中它们的具体实现:Iterator、iterator、Iterable、ListIterator、listIterator的区别:iterator 、listIterator 方法的基本使用和底层实现解读:ArrayList集合的 iterator 方法的底层实现源码解读:LinkedList 继承自 AbstractList 的 iterator 方法 源码解读:Ar的全部内容,希望文章能够帮你解决20:Iterator、iterator、Iterable、ListIterator、listIterator的使用及区别及源码中它们的具体实现:Iterator、iterator、Iterable、ListIterator、listIterator的区别:iterator 、listIterator 方法的基本使用和底层实现解读:ArrayList集合的 iterator 方法的底层实现源码解读:LinkedList 继承自 AbstractList 的 iterator 方法 源码解读:Ar所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(55)

评论列表共有 0 条评论

立即
投稿
返回
顶部