• Java迭代器原理


    1迭代器模式

    迭代器是一种设计模式,这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。

    一般实现方式如下:(来自

    public interface Iterator {
       public boolean hasNext();
       public Object next();
    }
    public interface Container {
       public Iterator getIterator();
    }
    public class NameRepository implements Container {
       public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};
    
       @Override
       public Iterator getIterator() {
          return new NameIterator();
       }
    
       private class NameIterator implements Iterator {
    
          int index;
    
          @Override
          public boolean hasNext() {
             if(index < names.length){
                return true;
             }
             return false;
          }
    
          @Override
          public Object next() {
             if(this.hasNext()){
                return names[index++];
             }
             return null;
          }        
       }
    }
    public class IteratorPatternDemo {
        
       public static void main(String[] args) {
          NameRepository namesRepository = new NameRepository();
    
          for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){
             String name = (String)iter.next();
             System.out.println("Name : " + name);
          }     
       }
    }

    一般情况,我们自己开发时很少自定义迭代器,因为java本身已经把迭代器做到内部中了

    2Java中的迭代器

    (1)Iterator接口

    package java.util;
    
    import java.util.function.Consumer;
    
    public interface Iterator<E> {
    
        /**
    * 如果迭代器又更多的元素,返回true。 * 换句话说,如果next方法返回一个元素而不是抛出一个异常,则返回true。
    */
    boolean hasNext(); //返回迭代器中的下一个元素,如果没有,则抛出NoSuchElementException异常 E next(); /**
    * 从底层集合中删除该迭代器返回的最后一个元素(可选操作)。每执行一次next方法这个方法只能被调用1次。 * 如果在迭代过程中,除了调用此方法之外,任何其他方法修改基础集合,则迭代器的行为是不确定的。 * 默认实现是抛出一个UnsupportedOperationException异常,不执行其他操作。 * 如果每次调用该方法前next方法没有执行,则抛出IllegalStateException异常。
    */
    default void remove() { throw new UnsupportedOperationException("remove"); } /**
    *
    @since 1.8(函数编程)。 * 对每个剩余元素执行给定的操作,直到所有元素都被处理或动作抛出异常为止。 * 如果指定了该顺序,则按迭代顺序执行操作。动作抛出的异常被传递给调用者。 * 如果action为null,则抛出NullPointerException
    */
    default void forEachRemaining(Consumer<? super E> action) { Objects.requireNonNull(action); while (hasNext()) action.accept(next()); } }

    首先,Iterator接口是属于java.util包的。然后里面只有4个方法(用法介绍请看注释)。

    forEachRemaining方法是Java8函数编程新加入的。作用是对前游标之后的每个元素进行处理(没有返回值),具体怎么处理根据传入的函数。这项里操作使得迭代器更加灵活,操作粒度更加细致。

    补充:default关键字可以让接口中的方法可以有默认的函数体,当一个类实现这个接口时,可以不用去实现这个方法,当然,这个类若实现这个方法,就等于子类覆盖了这个方法,最终运行结果符合Java多态特性。(Java8的新特性)

    类注释:

    /**
     * An iterator over a collection.  {@code Iterator} takes the place of {@link Enumeration} in the Java Collections Framework.  Iterators
     * differ from enumerations in two ways:
     *
     * <ul>
     *      <li> Iterators allow the caller to remove elements from the underlying collection during the iteration with well-defined semantics.
     *      <li> Method names have been improved.
     * </ul>
     *
     * <p>This interface is a member of the <a href="{@docRoot}/../technotes/guides/collections/index.html">Java Collections Framework</a>.
     */

    Iterator是集合上的迭代器。在Java集合框架中Iterator用来替代Enumeration,Iterator与Enumeration有以下两点区别:

    • Iterator允许调用者通过定于语义良好的迭代器删除底层集合中的元素。
    • 方法名称已得到改进。

    这个接口是Java集合框架的成员。除了如上两点不同外,Java8版本Iterator还加入了函数式编程。

    (2)Iterable接口

    package java.lang;
    
    // 实现此接口,允许对象成为“for-each loop”语句的目标。
    public interface Iterable<T> {
        
    // 返回类型为 T元素的迭代器。 Iterator<T> iterator(); /** * @since 1.8
    * 对Iterable的每个元素执行给定的操作,直到所有元素都被处理或动作引发异常。
    * 除非实现类另有规定,否则按照迭代的顺序执行操作(如果指定了迭代顺序)。 动作抛出的异常被转发给调用者。
    * 抛出NullPointerException - 如果指定的动作为空
    */ default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } } /** * @since 1.8
    * 在Iterable描述的元素上创建一个Spliterator。Spliterator继承了迭代器的fail-fast属性。
    *
    */ default Spliterator<T> spliterator() { return Spliterators.spliteratorUnknownSize(iterator(), 0); } }

    Java集合包最常用的有Collection和Map两个接口的实现类。Map的实现类迭代器是内部实现的,而Collection继承了Iterable接口。

    这里以ArrayList为例,梳理一下Iterator的工作流程。

    ArrayList是Collection的子类,而Collection又实现了Iterable接口,Iterable接口里面有iterator()方法(该方法返回一个迭代器对象)。所以,ArrayList(或其父类)也必须实现iterator()方法。

    iterator()方法返回一个Iterator对象,而前文中Iterator是个接口。我们不能不知道具体用哪个实现类也不能直接new接口,所以我们找到其直接父类AbstractList,查看iterator()到底如何实现的:

     public Iterator<E> iterator() {
            return new Itr();
        }
    
    private class Itr implements Iterator<E> {
           
            // Index of element to be returned by subsequent call to next.
            int cursor = 0;
    
            /**
             * Index of element returned by most recent call to next or
             * previous.  Reset to -1 if this element is deleted by a call
             * to remove.
             */
            int lastRet = -1;
    
            /**
             * The modCount value that the iterator believes that the backing
             * List should have.  If this expectation is violated, the iterator
             * has detected concurrent modification.
             */
            int expectedModCount = modCount;
    
            public boolean hasNext() {
                return cursor != size();
            }
    
            public E next() {
                checkForComodification();
                try {
                    int i = cursor;
                    E next = get(i);
                    lastRet = i;
                    cursor = i + 1;
                    return next;
                } catch (IndexOutOfBoundsException e) {
                    checkForComodification();
                    throw new NoSuchElementException();
                }
            }
    
            public void remove() {
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();
    
                try {
                    AbstractList.this.remove(lastRet);
                    if (lastRet < cursor)
                        cursor--;
                    lastRet = -1;
                    expectedModCount = modCount;
                } catch (IndexOutOfBoundsException e) {
                    throw new ConcurrentModificationException();
                }
            }
    
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }

    这里是通过内部类实现了Iterator接口,然后再将其实例对象返回。

    原理很简单,每调用一次next方法,先返回当前游标指向位置的值,然后游标往下移动一位,直到游标数值等于list的size。

    而在ArrayList类里面又提供了一个实现版本:

        /**
         * An optimized version of AbstractList.Itr
         */
        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;
    
            Itr() {}
    
            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();
            }
        }

    next方法很好理解,和父类大概是一个意思。remove方法是调用ArrayList.this.remove(lastRet)实现:

    public E remove(int index) {
            rangeCheck(index);
    
            modCount++;
            E oldValue = elementData(index);
    
            int numMoved = size - index - 1;
            if (numMoved > 0)
                System.arraycopy(elementData, index+1, elementData, index,
                                 numMoved);
            elementData[--size] = null; // clear to let GC do its work
    
            return oldValue;
        }

    numMoved 是计算要移动的元素个数(删除数组的某一位置的值,后面的值要依次往前移)。

    cursor = lastRet;

    lastRet = -1;

    表示删除之后,游标前移1位。为什么这么做?举个例子,数组a = [1,2,3,4],若cursor = 2,游标指向数字3,则lastRet = 1。当删除a[1]的时候,a = [1,3,4]。3对应的位置变为1了,所以会有cursor = lastRet。

    lastRet的值置为-1,这里很好的解释了前面注释中为什么remove方法一定要在next方法之后执行了。

  • 相关阅读:
    mysql 更改字符集
    修改pip源
    git命令
    virtualwrapper使用
    Python环境搭建
    IntellIJ IDEA 配置 Maven 以及 修改 默认 Repository
    Spring4:JDBC
    Spring3:AOP
    Spring2:bean的使用
    Spring1:Spring简介、环境搭建、源码下载及导入MyEclipse
  • 原文地址:https://www.cnblogs.com/ouym/p/8857448.html
Copyright © 2020-2023  润新知