第十一章 持有对象1

 

 

Foreach与迭代器

自学java以来,我用到最方便的遍历方式莫属foreach了,也仅仅会用它,知道它能遍历数组还能便利集合(除Map外)。

foreach能遍历集合是在Java SE5才出现的,Java SE5引入了新的被称为Iterable的接口(java.lang.Iterable),这个接口仅包含一个能够产生Iterator的iterator()方法,并且Iterable接口被foreach用来在序列中移动。因此如果你创建了任何实现Iterable的类,都可以将他用于foreach语句中。

既然除Map外的所有集合(Collection对象)都能使用foreach,可想而知,Collection接口肯定是实现了Iterable接口。在Collection或其导出类中肯定对iterator()方法进行了重写,然后产生一个Iterator对象。

源码中其他不相干的我且删掉:

package java.util;

import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public interface Collection<E> extends Iterable<E> {/**
     * Returns an iterator over the elements in this collection.  There are no
     * guarantees concerning the order in which the elements are returned
     * (unless this collection is an instance of some class that provides a
     * guarantee).
     *
     * @return an <tt>Iterator</tt> over the elements in this collection
     */
    Iterator<E> iterator();

}

以最常用的ArrayList分析(已删减):

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{

    public Iterator<E> iterator() {
        return new 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];
        }

    }
}

iterator方法返回的是一个实现了Iterator<E>接口的内部类,没有使用匿名内部类的原因可想而知,Itr内部类肯定需要被再次使用。ArrayList实现了iterator()方法,该方法提供了对ArrayList的迭代。所以foreach对ArrayList的迭代即是间接的调用了iterator方法,实现遍历,从代码中你也可以看到为什么foreach只能从前向后遍历,它只有next()方法。至于Set接口下的导出类看上去很复杂,但是本质上都是一样,在它们内部也必定以某种方式实现了iterator方法。

 

foreach对于Collection的遍历是对Iterable接口的移动,那foreach对数组的遍历是不是也对Iterable接口的移动?数组作为最基本最常用的存储方式,你找不到任何它是否实现了Iterable接口的说明,但是可以用一个例子测一下:

package mvn.text;

import java.util.Arrays;

public class TestForeach {
    public static void main(String[] args) {
        Integer[] i = {1,2,3,4,5};
        
        //测试ArrayList
        test(Arrays.asList(i));
        
        //测试数组
        //test(i);
    }
    
    public static <T> void test(Iterable<T> itr) {
        for (T t : itr) {
            System.out.print(t + " ");
        }
    }
}

 把数组直接代进test函数里发现,编译都通不过。这说明不存在任何从数组到Iterable的自动转换。

那foreach是如何是实现数组的遍历?

猜想:java的设计者们是否是在使用foreach遍历数组时直接将其转换为普通for循环for(int i = 0 ; i < str.length ; i++) { } ?至少看起来这样转换更加合理一些,扩展了对数组的遍历方式,就像使用适配器模式一样,给普通的for循环替换成另一种接口foreach的样式,但根本上还是使用普通for循环而已。

posted @ 2018-06-26 13:59  刘呆哗  阅读(120)  评论(0编辑  收藏  举报