1.前言
在面試的時(shí)候,如果面試官問你:Iterator 和 Iterable 有什么區(qū)別,你會(huì)怎么回答這個(gè)問題呢?要是一年前的我來回答這個(gè)問題的話,估計(jì)我直接就如鯁在喉、啞口無言了,不過現(xiàn)在的我還是能跟面試官聊上幾句的,那么今天就和各位小伙伴分享一下我對(duì) Iterator 和 Iterable 的理解。
2.Iterator 和 Iterable
定義
在聊 Iterator 和 Iterable 之前,我們得先明白二者的定義,俗話說“繁瑣問題必有猥瑣解法”,我們用一個(gè)“猥瑣”的方式來理解二者的含義
Iterable:英語好的小伙伴應(yīng)該都知道,以 able 結(jié)尾的單詞,表示的含義都是【可以…】或【支持…】,那么 Iterable 所代表的含義也就是可迭代的、支持迭代的,因此我們可以知道,實(shí)現(xiàn)了 Iterable 接口的對(duì)象是支持迭代,是可迭代的。
Iterator:在英語中以 or 結(jié)尾的單詞表示的含義是【 …樣的人】或【 …者】,就像 creator 表示的是創(chuàng)作者的意思。那么這里也是一樣的,iterator 就是迭代者,我們一般叫迭代器,它就是提供迭代機(jī)制的對(duì)象,具體如何迭代,都是 Iterator 接口來規(guī)范的。
P.S. 在 Java 設(shè)計(jì)模式中也有一種模式叫迭代器模式,Iterator 就是迭代器模式的一個(gè)應(yīng)用例子
Iterable源碼及方法
Iterable 源碼
public interface Iterable<T> {
/**
* Returns an iterator over elements of type {@code T}.
*
* @return an Iterator.
*/
Iterator<T> iterator();
/**
* Performs the given action for each element of the {@code Iterable}
* until all elements have been processed or the action throws an
* exception. Unless otherwise specified by the implementing class,
* actions are performed in the order of iteration (if an iteration order
* is specified). Exceptions thrown by the action are relayed to the
* caller.
*
* @implSpec
* <p>The default implementation behaves as if:
* <pre>{@code
* for (T t : this)
* action.accept(t);
* }</pre>
*
* @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);
}
}
/**
* Creates a {@link Spliterator} over the elements described by this
* {@code Iterable}.
*
* @implSpec
* The default implementation creates an
* <em><a href="Spliterator.html#binding">early-binding</a></em>
* spliterator from the iterable's {@code Iterator}. The spliterator
* inherits the <em>fail-fast</em> properties of the iterable's iterator.
*
* @implNote
* The default implementation should usually be overridden. The
* spliterator returned by the default implementation has poor splitting
* capabilities, is unsized, and does not report any spliterator
* characteristics. Implementing classes can nearly always provide a
* better implementation.
*
* @return a {@code Spliterator} over the elements described by this
* {@code Iterable}.
* @since 1.8
*/
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
通過源碼我們可以到 Iterable 接口中有三個(gè)方法,分別是:
Iterator iterator() :該接口主要是用來返回T類型的元素上的一個(gè)迭代器。
default void forEach(Consumer action) :該方法是循環(huán)輸出,對(duì)內(nèi)部元素進(jìn)行遍歷并對(duì)元素進(jìn)行指定的操作。
default Spliterator spliterator():該方法提供了一個(gè)可以并行遍歷元素的迭代器,以適應(yīng)現(xiàn)在cpu多核時(shí)代并行遍歷的需求。
Iterator 源碼及方法
Iterator源碼
public interface Iterator<E> {
/**
* Returns {@code true} if the iteration has more elements.
* (In other words, returns {@code true} if {@link #next} would
* return an element rather than throwing an exception.)
*
* @return {@code true} if the iteration has more elements
*/
boolean hasNext();
/**
* Returns the next element in the iteration.
*
* @return the next element in the iteration
* @throws NoSuchElementException if the iteration has no more elements
*/
E next();
/**
* Removes from the underlying collection the last element returned
* by this iterator (optional operation). This method can be called
* only once per call to {@link #next}. The behavior of an iterator
* is unspecified if the underlying collection is modified while the
* iteration is in progress in any way other than by calling this
* method.
*
* @implSpec
* The default implementation throws an instance of
* {@link UnsupportedOperationException} and performs no other action.
*
* @throws UnsupportedOperationException if the {@code remove}
* operation is not supported by this iterator
*
* @throws IllegalStateException if the {@code next} method has not
* yet been called, or the {@code remove} method has already
* been called after the last call to the {@code next}
* method
*/
default void remove() {
throw new UnsupportedOperationException("remove");
}
/**
* 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.
*
* @implSpec
* <p>The default implementation behaves as if:
* <pre>{@code
* while (hasNext())
* action.accept(next());
* }</pre>
*
* @param action The action to be performed for each element
* @throws NullPointerException if the specified action is null
* @since 1.8
*/
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
通過源碼我們可以看到,Iterator 中包含了四個(gè)方法,分別是:
boolean hasNext():判斷需要遍歷的集合是否存在下一個(gè)元素,即如果被迭代遍歷的集合還沒有被遍歷完,返回 true
E next():返回集合里面的下一個(gè)元素
default void remove():刪除集合里面上一次 next() 方法返回的元素
forEachRemaining(Consumer action):該方法是JDK 1.8后新增默認(rèn)方法,含義是使用Lambda表達(dá)式來遍歷集合元素
看到這可能有些小伙伴就會(huì)問了:default void forEachRemaining(Consumer action) 方法怎么用呢?它和 forEach() 方法又有什么區(qū)別呢?我們接著往下看~
forEachRemaining 方法是使用就很簡(jiǎn)單了,直接給大家貼一個(gè)示例代碼
public static void main(String[] args) {
List list = new ArrayList<String>();
list.add("我在");
list.add("人民廣場(chǎng)");
list.add("吃著炸雞");
list.iterator().forEachRemaining(str-> System.out.println(str));
}
關(guān)于二者的區(qū)別也很簡(jiǎn)單,我們通過源碼就可以一目了然~
forEachRemaining()源碼
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
forEach()源碼
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
這兩個(gè)方法說是跟孫悟空和六耳獼猴一樣,二者長(zhǎng)得太像了,他們都可以遍歷集合而且都是接口的默認(rèn)方法,唯一不同的地方就是循環(huán)方式不一樣。前者內(nèi)部是通過使用迭代器來遍歷的所有元素;而后者內(nèi)部使用的是增強(qiáng) for 循環(huán)遍歷的元素,不同的遍歷方式也導(dǎo)致了 forEach() 方法可以多次調(diào)用,而 forEachRemaining() 方法在第二次調(diào)用時(shí)不會(huì)做任何操作,因?yàn)椴粫?huì)有下一個(gè)元素了。
Iterator 和 Iterable 的區(qū)別
通過上面的講解,相信各位小伙伴對(duì) Iterator 和 Iterable 有了一些自己的見解,最后我們?cè)僖黄鹂偨Y(jié)一下二者之間的區(qū)別。
Iterator 是迭代器類,而 Iterable 是一個(gè)接口,約束某類是否可迭代,某個(gè)類只要實(shí)現(xiàn)了 Iterable 接口就可以使用 foreach 進(jìn)行迭代。同時(shí) Iterable 中又封裝了 Iterator 接口,只要實(shí)現(xiàn)了 Iterable 接口的類,也就可以使用 Iterator 迭代器了。集合Collection、List、Set都是 Iterable 的實(shí)現(xiàn)類,所以他們及其他們的子類都可以使用 foreach 進(jìn)行迭代。
上面我們提到了 Collection、List、Set 都是 Iterable 的實(shí)現(xiàn)類,那為什么一定要去實(shí)現(xiàn) Iterable 這個(gè)接口呢?直接實(shí)現(xiàn) Iterator 接口不行嗎?答案肯定是不行的,原因也很簡(jiǎn)單
Iterator 接口中的核心方法 next() 或 hasNext() 是依賴于迭代器的當(dāng)前迭代位置的。如果Collection直接實(shí)現(xiàn)Iterator接口,則導(dǎo)致集合對(duì)象中包含當(dāng)前迭代位置的數(shù)據(jù)(可以理解成指針)。當(dāng)集合在不同方法間被傳遞時(shí),由于當(dāng)前迭代位置不可預(yù)知,那么 next() 方法的結(jié)果也就成了一個(gè)未知數(shù);而 Iterable 則不然,每次調(diào)用都會(huì)返回一個(gè)從頭開始計(jì)數(shù)的迭代器,并且多個(gè)迭代器是互不干擾的,所以 Collection、List、Set 集合實(shí)現(xiàn)的是 Iterable 而非 Iterator 。
總結(jié)
經(jīng)驗(yàn)有限,有些地方可能講的沒有特別到位,如果文章中有錯(cuò)誤,歡迎大家留言指正;若您有更好、更獨(dú)到的理解,歡迎您留下寶貴想法。