我是靠谱客的博主 现代火,最近开发中收集的这篇文章主要介绍集合之IteratorIterator迭代器接口,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

  • Iterator迭代器接口
    • Enumeration
    • Iterator
      • Iterator 和 Enumeration 区别
      • Iterator源码详解
      • Iterator的使用
    • Iterator的子接口ListIterator
      • ListIterator获取方式
      • ListIterator实现
    • forEachRemaining() 方法的用法

Iterator迭代器接口

在这里插入图片描述

  • Collection 接口继承了 java.lang.Iterable 接口,该接口有一个iterator()方法,所有实现了Collection接口的集合类都有一个iterator()方法,用于返回一个实现了Iterator接口的对象
  • Iterable 接口中有三个方法:
    public interface Iterable<T> {
        /*
         ** 返回类型T元素的迭代器
         */
        Iterator<T> iterator();
 
        /**
         * 对 Iterable的每个元素执行给定的操作,直到所有元素都被处理或引发异常
         */
        default void forEach(Consumer<? super T> action) {
            Objects.requireNonNull(action);
            for (T t : this) {
                action.accept(t);
            }
        }
    
        /**
         * 在Iterable描述的元素上创建一个Iterable 
         */
        default Spliterator<T> spliterator() {
            return Spliterators.spliteratorUnknownSize(iterator(), 0);
        }
    }

Enumeration

  • 在了解 Iterator 之前先看一下它的前身 Enumeration<E>Enumeration 接口是Iterator迭代器的“古老版本”,有两个方法:
    public interface Enumeration<E> {
        /**
         * 是否还有元素
         */
        boolean hasMoreElements();
    
        /**
         * 返回下一个元素
         */
        E nextElement();
    }
  • 通过上面两个两个方法,可以用来遍历实现类的元素:
    Enumeration st = new StringTokenizer("this is a test");
    while (st.hasMoreElements()) {
        System.out.println(st.nextElement());
    }

运行结果:
在这里插入图片描述

Iterator

在这里插入图片描述

  • Iterator 对象称为迭代器(设计模式的一种),用于遍历 Collection 集合中的元素
  • GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生
  • Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建Iterator 对象,则必须有一个被迭代的集合
  • 集合对象每次调用 iterator() 方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前
  • 用来替代 Enumeration 进行遍历、迭代

Iterator 和 Enumeration 区别

  • 官方定义:

Iterators differ from enumerations in two ways:
Iterators allow the caller to remove elements from the underlying collection during the iteration with well-defined semantics.
Method names have been improved.

  • 翻译为中文:
  1. 迭代器允许调用者在迭代期间使用定义明确的语义从底层集合中删除元素
  2. 方法名称得到改进

Iterator源码详解

    public interface Iterator<E> {
        /**
         * 是否有下一个元素
         */
        boolean hasNext();
    	
        /**
         * 返回迭代中的下一个元素
         * @return 迭代中的下一个元素
         * @throws NoSuchElementException
         */
        E next();
    
        /**
         * 从基础集合中移除这个迭代器返回的最后一个元素
         * @throws UnsupportedOperationException
         * @throws IllegalStateException
         */
         default void remove() {
            throw new UnsupportedOperationException("remove");
        }
        
        /**
         * 对每个剩余元素执行给定的操作,直到所有元素都已处理或该操作引发异常
         * 如果指定了迭代顺序,则按迭代顺序执行操作。操作引发的异常将转发给调用者
         * @throws NullPointerException
         */
        default void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            while (hasNext())
                action.accept(next());
        }
    }
  • 从源码中可以看出,next()remove() 方法可能抛出异常,抛出异常原因:

在这里插入图片描述

  • 在调用it.next()方法之前必须要调用it.hasNext()进行检测。若不调用,且下一条记录无效,直接调用it.next()会抛出
    NoSuchElementException 异常
  • Iterator可以删除集合的元素,但是是遍历过程中通过迭代器对象的remove方法,不是集合对象的remove方法
  • 如果还未调用next()或在上一次调用next方法之后已经调用了remove方法,再调用remove都会报IllegalStateException

Iterator的使用

	Iterator it = list.iterator();
	while (it.hasNext()){
	    System.out.println(it.next());
	}

Iterator的子接口ListIterator

在这里插入图片描述

  • Iterator的子接口ListIterator在它的基础上又添加了几种方法:
方法作用
hasPrevious()判断游标前是否有元素
previous()返回游标前面的元素,同时游标前移一位,如果游标前没有元素报java.util.NoSuchElementException,使用前应该调用 hasPrevious() 判断
previousIndex()返回游标前面元素的位置,初始时为 -1,同时报 java.util.NoSuchElementException
nextIndex()返回游标后边元素的索引位置,初始为 0 ;遍历 N 个元素结束时为 N
add(E e)在游标前面插入一个元素
set(E e)更新迭代器最后一次操作的元素为 E,也就是更新最后一次调用 next() 或者 previous() 返回的元素,当没有迭代,如果没有调用 next() 或者 previous() 直接调用 set 时,报java.lang.IllegalStateException
  • 如果实现Iterator接口,那么在遍历集合中元素的时候,只能往后遍历,被遍历到的元素不会再次遍历到,通常无序集合实现的都是这个接口,比如HashSetHashMap;而那些元素有序的集合,实现的一般都是ListIterator接口,实现这个接口的集合可以双向遍历,既可以通过next()访问下一个元素,又可以通过previous()访问前一个元素,比如ArrayList
  • 迭代器没有当前所在元素一说,它只有一个游标( cursor )的概念,这个游标总是在元素之间:
    在这里插入图片描述
  • 初始时它在第 0 个元素之前,调用 next() 游标向后移一位: 在这里插入图片描述
  • 调用 previous() 游标就会回到之前位置。当向后遍历完元素,游标就会在元素 N 的后面 在这里插入图片描述
  • 就是说长度为 N 的集合会有 N+1 个游标的位置

ListIterator获取方式

    List.listIterator();
    List.listIterator(int location);// 可以指定游标的所在位置

ListIterator实现

在这里插入图片描述

  • AbstractList 作为 List 的直接子类,里面实现了 listIterator() 方法,并且有两个内部迭代器实现类:ListItrSubList

在这里插入图片描述

  • listIterator() 返回的是 ListItr()
    public ListIterator<E> listIterator(final int index) {
        rangeCheckForAdd(index);
    
        return new ListItr(index);
    }
  • ListItr 继承了 ItrItr 实现了 Iterator 接口:
    private class Itr implements Iterator<E> {
        /**
         * 后续调用 next 将返回的元素索引
         */
        int cursor = 0;
    
        /**
         * 最近调用下一个或上一个返回的元素的索引。如果调用 remove 删除了此元素,则重置为 -1
         */
        int lastRet = -1;
    
        /**
         * 用来判断是否 fail-fast 的变量,迭代器认为支持 List 应该具有的 modCount 值
         * 如果违反此预期,则迭代器已检测到并发修改
         */
        int expectedModCount = modCount;
        
        // 当游标没有跑到最后一个元素后面时 hasNext 返回 true
        public boolean hasNext() {
            return cursor != size();
        }
        
        // 获取下一个元素
        public E next() {
            // 判断并发修改,存在抛出ConcurrentModificationException
            checkForComodification();
            try {
                int i = cursor;
                // 获取游标所在元素,具体子类有具体实现
                E next = get(i);
                lastRet = i;
                // 更新游标
                cursor = i + 1;
                return next;
            } catch (IndexOutOfBoundsException e) {
                // 先判断是否并发修改,不存在抛出NoSuchElementException
                checkForComodification();
                throw new NoSuchElementException();
            }
        }
        
        // 删除上次迭代操作的元素
        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            // 判断并发修改
            checkForComodification();
    
            try {
                // 调用子类实现的删除操作
                AbstractList.this.remove(lastRet);
                // 游标大于当前元素索引,游标-1
                if (lastRet < cursor)
                    cursor--;
                // 删除元素,重置为 -1
                lastRet = -1;
                // 同步modCount
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }
        
        // 判断是否并发修改
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
  • 再看下 ListItr 具体实现:
    private class ListItr extends Itr implements ListIterator<E> {
        // 构造函数指定游标
        ListItr(int index) {
            cursor = index;
        }
        
        // 当游标不为0时返回true
        public boolean hasPrevious() {
            return cursor != 0;
        }
    
        // 获取游标前一个元素
        public E previous() {
            // 判断并发修改
            checkForComodification();
            try {
                // 游标-1并获取之后索引所在元素
                int i = cursor - 1;
                E previous = get(i);
                // 更新游标及索引
                lastRet = cursor = i;
                return previous;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }
    
        // 下次调用next()返回元素索引,就是游标
        public int nextIndex() {
            return cursor;
        }
        
        // 下次调用previous()返回元素索引,就是游标-1
        public int previousIndex() {
            return cursor-1;
        }
    
        public void set(E e) {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
    
            try {
                // 调用子类根据索引修改元素
                AbstractList.this.set(lastRet, e);
                // 同步modCount
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
        
        // 添加元素
        public void add(E e) {
            checkForComodification();
    
            try {
                // 记录游标
                int i = cursor;
                // 调用子类指定索引添加元素
                AbstractList.this.add(i, e);
                lastRet = -1;
                // 游标+1
                cursor = i + 1;
                // 同步modCount
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    }
  • 可以看到 ListItr 的主要操作最后都交给子类来实现,List 的子类 ArrayList, LinkedList, Vector 由于底层实现原理不同(数组,双向链表),具体操作类也会有所不同

forEachRemaining() 方法的用法

  • 官方文档

Performs the given action for each remaining element until all
elements have been processed or the action throws an exception.
Actions are performed in the order of iteration, if that order is
specified. Exceptions thrown by the action are relayed to the caller.

  • 翻译后

对每个剩余元素执行给定的操作,直到所有元素都已处理或该操作引发异常。如果指定了迭代顺序,则按迭代顺序执行操作。操作引发的异常将转发给调用者

  • 就是对集合中剩余的元素进行操作,直到元素完毕或者抛出异常
    public static void main(String[] args) {
        // 创建一个元素类型为String的集合
        List<String> list = Stream.of("G", "o", "l", "d", "e", "n", "S", "t", "a", "r").collect(Collectors.toList());
    
        // 获取迭代器
        Iterator<String> iterator = list.iterator();
        iterator.forEachRemaining(System.out::println);
    }

可以看到就是正常迭代过程:
在这里插入图片描述

  • 修改代码如下:
    public static void main(String[] args) {
        // 创建一个元素类型为String的集合
        List<String> list = Stream.of("G", "o", "l", "d", "e", "n", "S", "t", "a", "r").collect(Collectors.toList());
    
        // 获取迭代器
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    
        System.out.println("------forEachRemaining(Consumer<? super E> action)--------");
        // 调用 forEachRemaining 方法遍历
        iterator.forEachRemaining(System.out::println);
    }
  • 调用了两个遍历方法,发现只迭代了一次,当第一次调用迭代器遍历集合元素时,迭代器就已经将元素遍历完毕,也就是说迭代器中已经没有剩余元素了,因此这时调用forEachRemaining()方法,就什么也不输出了
    在这里插入图片描述
  • 修改代码验证
    public static void main(String[] args) {
        // 创建一个元素类型为String的集合
        List<String> list = Stream.of("G", "o", "l", "d", "e", "n", "S", "t", "a", "r").collect(Collectors.toList());
    
        // 获取迭代器
        Iterator<String> iterator = list.iterator();
        String str;
        while (iterator.hasNext()) {
            str = iterator.next();
            System.out.println(str);
            if ("n".equals(str)) {
                break;
            }
        }
    
        System.out.println("------forEachRemaining(Consumer<? super E> action)--------");
        // 调用 forEachRemaining 方法遍历
        iterator.forEachRemaining(System.out::println);
    }
  • 可以看到,当第一次用迭代器遍历时,只让它遍历到 n 就跳出循环,剩下的元素再调用 forEachRemaining() 方法,就可以看到输出后面元素了
    在这里插入图片描述
  • 在其中抛出异常:
    public static void main(String[] args) {
        // 创建一个元素类型为String的集合
        List<String> list = Stream.of("G", "o", "l", "d", "e", "n", "S", "t", "a", "r").collect(Collectors.toList());
    
        // 获取迭代器
        Iterator<String> iterator = list.iterator();
        String str;
        while (iterator.hasNext()) {
            str = iterator.next();
            System.out.println(str);
            if ("n".equals(str)) {
                break;
            }
        }
    
        System.out.println("------forEachRemaining(Consumer<? super E> action)--------");
        // 调用 forEachRemaining 方法遍历
        iterator.forEachRemaining(it -> {
            if ("a".equals(it)) {
                throw new RuntimeException("Manual error");
            }
            System.out.println(it);
        });
    }
  • 当第一次用迭代器遍历时,只让它遍历到 n 就跳出循环,剩下的元素再调用 forEachRemaining() 方法,然后在 forEachRemaining() 方法中手动抛出异常,可以看到有异常之后的元素没有输出
    在这里插入图片描述

最后

以上就是现代火为你收集整理的集合之IteratorIterator迭代器接口的全部内容,希望文章能够帮你解决集合之IteratorIterator迭代器接口所遇到的程序开发问题。

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

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

评论列表共有 0 条评论

立即
投稿
返回
顶部