详解java集合框架中的迭代器模式(Iterator接口 Iterable接口)

设计模式系列之 迭代器模式中介绍了迭代器模式的基本知识,该模式主要包含了抽象迭代器、具体迭代器、抽象聚合类、具体聚合类。前两个类规范了对聚合类的遍历方式,后两个类主要用于存储数据和提供对应的迭代器。java集合框架涉及的众多类,譬如ArrayList、HashMap等,都可称为用于存储数据的聚合类,并且java的作者也为其提供了对应迭代器。

随着java语言的版本更迭,其集合框架使用迭代器模式的方式同样发生了变化。本文基于不同版本JDK,分别介绍迭代器模式在java集合框架(除去Map相关接口)中应用。

JDK1.2中的迭代器

在该版本的集合框架中,尚未出现Iterable接口。CoLlection接口是所有集合类的基类。分别截取部分Collection接口、Iterator接口、ArrayList类、Itr类(ArrayList私有内部类)的部分源码,可以发现Collection接口、Iterator接口、ArrayList类均出自jdk1.2。

  1. 部分Collection接口源码。注意此时JDK中没有Iterable类

     /*
     * @since 1.2
     */
    public interface Collection<E>{
        //定义返回迭代器的方法
       Iterator<E> iterator();
    }
    
  2. 部分Iterator接口源码

    /** 
     * @since 1.2
     */
    public interface Iterator<E> {
        boolean hasNext();
        E next();
    }
    
  3. 部分ArrayList类源码

    /**
     * @since   1.2
     */
    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    {
        public Iterator<E> iterator() {
                return new Itr();
            }
    }
    
  4. 部分Itr类源码

    //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;
    
            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];
            }
        }
    

参照迭代器模式,此时的

  • Collection接口相当于迭代器模式中的抽象聚合类,该聚合类中定义了一个返回迭代器的方法;
  • Iterator接口相当于迭代器模式中的抽象迭代器类,定义了实现迭代的关键方法;
  • ArrayList类相当于具体的聚合类,返回了一个具体迭代器Itr实例;
  • Itr类相当于具体的迭代器类,实现了迭代逻辑;

同迭代器模式不同的是,具体的迭代器类Itr不是一个独立的类,而是作为ArrayList类的私有内部类,但Itr类所处的位置不影响迭代器模式发挥其存储与遍历分离的解耦优势。

JDK1.5之后的迭代器

在jdk1.5中,新增了Iterable接口,并且Collection继承了该接口,因此Iterable接口成为集合框架的最上层基类。该接口的接口描述介绍了其主要作用“实现该接口的类可以使用foreach循环”。

  1. 部分Iterable接口源码

    /**
     * Implementing this interface allows an object to be the target of
     * the "for-each loop" statement. (实现该接口的类可以使用foreach循环)
     * @since 1.5
     */
    public interface Iterable<T> {
        /**
         * @return an Iterator.
         */
        Iterator<T> iterator();
    
        /**
         * @param action The action to be performed for each element
         * @throws NullPointerException if the specified action is null
         * @since 1.8
         */
        default void forEach(Consumer<? super T> action) {
            Objects.requireNonNull(action);
            for (T t : this) {
                action.accept(t);
            }
        }
    
        /**
         * @return a {@code Spliterator} over the elements described by this
         * {@code Iterable}.
         * @since 1.8
         */
        default Spliterator<T> spliterator() {
            return Spliterators.spliteratorUnknownSize(iterator(), 0);
        }
    }
    
    
  2. 部分Colleciton接口源码

    /** 
     * @since 1.2
     */
    public interface Collection<E> extends Iterable<E> {
        Iterator<T> iterator();
    }
    

虽然Collection接口继承了Iterable接口,并且Iterable中只有三个方法,但这并不影响以Iterable接口为基类的集合框架仍旧符合迭代器模式,此时Iterable接口可以认为是迭代器模式中抽象聚合类。其他三个角色同jdk1.2中基本一致。

Iterable接口和Iterator接口的区别

Itearable接口的源码注释交待了该类的主要作用,即新增该接口是为了达到”实现该接口的类可以使用foreach语句“的目的,强调了实现Iterable接口和使用foreach语法遍历的因果关系,与Iterator接口无关。Iterator接口是基于迭代器模式,实现了对聚合类的存储和遍历的分离。两者的拼写类似,但各自强调的内容不同。

  • iterator接口强调其实现类可使用迭代器遍历
  • iterable接口强调其实现类不仅可以使用迭代器遍历,也可以使用foreach语法遍历。

譬如,对于Collection的引用对象,在jdk1.5之前只有一种遍历方式,只能使用对应的迭代器访问其元素。而在jdk1.5之后,即继承了Iterable接口之后,有两种遍历方式,一种使用迭代器,一种使用foreach语法。

对网上关于Collection接口为什么不直接继承Iterator接口?的讨论,可以站在迭代器模式的角度回答,Collection接口属于抽象聚合角色,Iterator接口属于抽象迭代器角色,为实现存储与遍历分离,两者本就不属于同一继承链,只是存在依赖关系。即使在java1.5版本中Collection接口继承了Iterable接口,Iterable接口和Iterator仍是只存在依赖关系【依赖关系指在迭代器模式中,Iterable作为抽象聚合角色依赖抽象迭代器角色Iterator】,Iterable接口和Iterator接口只是拼写和含义相近而已。假设Collection接口继承Iterator接口,此时已不再符合迭代器模式的模式结构,也就无法达到迭代器模式的优点。

延申

  • 快速失败
    代码原因:在foreach中使用集合类(arraylist等)的remove/add方法
    现象:抛出Concurrent Modification Exception
    底层原因:modCount!=expectedmodCount ,集合类在执行remove方法时,只维护modCount,不维护expecedCount,造成两个数不相等。
    正确操作:使用迭代器(arraylist的itr迭代器等)的remove方法

参考资料

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值