• Java 集合:迭代器(Iterator, Iterable)


    Iterator接口

    public interface Iterator<E> {
    
        boolean hasNext();
    
        E next();
    
        void remove();
    }

    访问元素前需要使用hasNext进行判断是否有元素存在,如果有再通过next操作获取,直接使用next操作而不进行hasNext检测,当到达末尾时会抛出NoSuchElement异常

    Iterator的remove操作

    好久没有看JDK代码了,今天翻看Java Core看到迭代器里面的注意点,居然一点都回忆不起来了。先看如下代码:

            Iterator<String> iter = list.iterator();
            String s = iter.next();
            iter.remove();

    那么这里iter.remove()删除的是哪个元素,删除的是列表中的第一个元素,通用一点来讲是迭代器上一次next()所返回的那个元素。又有如下代码:

            Iterator<String> iter = list.iterator();
            String s = iter.next();
            iter.remove();
            iter.remove();

    如果去实际运行的话会报:java.lang.IllegalStateException异常即,每次remove都应该有对应的一次next,其实就是两两配对的,remove的就是next返回的那个元素。

    从AbstractList的源码中可以看到Iterator的一个基本实现:

     1 private class Itr implements Iterator<E> {
     2         /**
     3          * Index of element to be returned by subsequent call to next.
     4          */
     5         int cursor = 0;
     6 
     7         /**
     8          * Index of element returned by most recent call to next or
     9          * previous.  Reset to -1 if this element is deleted by a call
    10          * to remove.
    11          */
    12         int lastRet = -1;
    13 
    14         /**
    15          * The modCount value that the iterator believes that the backing
    16          * List should have.  If this expectation is violated, the iterator
    17          * has detected concurrent modification.
    18          */
    19         int expectedModCount = modCount;
    20 
    21         public boolean hasNext() {
    22             return cursor != size();
    23         }
    24 
    25         public E next() {
    26             checkForComodification();
    27             try {
    28                 int i = cursor;
    29                 E next = get(i);
    30                 lastRet = i;
    31                 cursor = i + 1;
    32                 return next;
    33             } catch (IndexOutOfBoundsException e) {
    34                 checkForComodification();
    35                 throw new NoSuchElementException();
    36             }
    37         }
    38 
    39         public void remove() {
    40             if (lastRet < 0)
    41                 throw new IllegalStateException();
    42             checkForComodification();
    43 
    44             try {
    45                 AbstractList.this.remove(lastRet);
    46                 if (lastRet < cursor)
    47                     cursor--;
    48                 lastRet = -1;
    49                 expectedModCount = modCount;
    50             } catch (IndexOutOfBoundsException e) {
    51                 throw new ConcurrentModificationException();
    52             }
    53         }
    54 
    55         final void checkForComodification() {
    56             if (modCount != expectedModCount)
    57                 throw new ConcurrentModificationException();
    58         }
    59     }

    可以看到有lastRet和cursor两个变量,前者用于代表next()操作返回的元素的索引,后者用于表示下一次next()调用是应该返回的元素的索引值。每当一次remove操作后lastRet就被清空了,同时cursor--,因为lastRet对应的元素在cursor前面,而此时其被remove了,那么cursor的值必然要减一。其实这里的迭代器实现都基本上被AbstractList的子类覆盖了,如LinkedList,ArrayList。前者不支持随机访问肯定不能用索引值作为获取元素的实现,否则迭代器效率就太低了。

    ListIterator(extends Iterator<E>)

    List接口除了继承Iterable接口外,还有几个额外的方法(listIterator)用来获取专门针对List的迭代器(即ListIterator)可以看一下LinkedList的迭代器实现:

    private class ListItr implements ListIterator<E> {
            private Node<E> lastReturned = null;
            private Node<E> next;
            private int nextIndex;
            private int expectedModCount = modCount;
    
            ListItr(int index) {
                // assert isPositionIndex(index);
                next = (index == size) ? null : node(index);
                nextIndex = index;
            }
    
            public boolean hasNext() {
                return nextIndex < size;
            }
    
            public E next() {
                checkForComodification();
                if (!hasNext())
                    throw new NoSuchElementException();
    
                lastReturned = next;
                next = next.next;
                nextIndex++;
                return lastReturned.item;
            }
    
            public boolean hasPrevious() {
                return nextIndex > 0;
            }
    
            public E previous() {
                checkForComodification();
                if (!hasPrevious())
                    throw new NoSuchElementException();
    
                lastReturned = next = (next == null) ? last : next.prev;
                nextIndex--;
                return lastReturned.item;
            }
    
            public int nextIndex() {
                return nextIndex;
            }
    
            public int previousIndex() {
                return nextIndex - 1;
            }
    
            public void remove() {
                checkForComodification();
                if (lastReturned == null)
                    throw new IllegalStateException();
    
                Node<E> lastNext = lastReturned.next;
                unlink(lastReturned);
                if (next == lastReturned)
                    next = lastNext;
                else
                    nextIndex--;
                lastReturned = null;
                expectedModCount++;
            }
    
            public void set(E e) {
                if (lastReturned == null)
                    throw new IllegalStateException();
                checkForComodification();
                lastReturned.item = e;
            }
    
            public void add(E e) {
                checkForComodification();
                lastReturned = null;
                if (next == null)
                    linkLast(e);
                else
                    linkBefore(e, next);
                nextIndex++;
                expectedModCount++;
            }
    
            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
        }

    对于remove操作的思路大体一致只不过把lastRet换成了一个链表节点lastReturned,在每次remove后也会将其置位null。而在获取元素上不是像父类版本中的那样直接通过get(i)进行获取。迭代器会保存两个相邻的节点指针lastReturned和next。这样当元素被remove掉(lastReturned=null),当再次调用next时由于保存了next指针值,依然可以在链表中移动。

    相比于Iterator接口ListIterator接口多了一个add方法,它会把元素放入到迭代器指向的next元素之前的位置,即下一个元素之前的位置。

    Iterable接口

    public interface Iterable<T> {
    
        /**
         * Returns an iterator over a set of elements of type T.
         *
         * @return an Iterator.
         */
        Iterator<T> iterator();
    }

    如Java Core上所述如果我们实现Iterable接口那么就可以在foreach循环中使用。如

    class MyCollection implements Iterable<Integer> {
    
        @Override
        public Iterator<Integer> iterator() {
            return new Iterator<Integer>() {
                public int count = 0;
    
                @Override
                public boolean hasNext() {
    
                    return count < 10;
                }
    
                @Override
                public Integer next() {
                    return count++;
                }
    
                @Override
                public void remove() {
                    throw new UnsupportedOperationException();
                }
    
            };
        }
    
    }
    
    public class Fields implements Const {
        public static void main(final String[] args) {
    
            MyCollection myCollection = new MyCollection();
            for (Integer i : myCollection) {
                System.out.println(i);
            }
    
        }
    }
  • 相关阅读:
    php 下载保存文件保存到本地的两种实现方法
    MySQL select语句直接导出数据
    Go学习笔记03-附录
    Go学习笔记02-源码
    Go学习笔记01-语言
    Go语言极速入门手册
    最简单的抓取网络图片或音乐文件
    使用PHP生成PDF文档
    Oracle常用函数
    ORACLE常用数值函数、转换函数、字符串函数
  • 原文地址:https://www.cnblogs.com/lailailai/p/4328407.html
Copyright © 2020-2023  润新知