1.前言
在面試的時候摆昧,如果面試官問你:Iterator 和 Iterable 有什么區(qū)別撩满,你會怎么回答這個問題呢蜒程?要是一年前的我來回答這個問題的話绅你,估計我直接就如鯁在喉、啞口無言了昭躺,不過現(xiàn)在的我還是能跟面試官聊上幾句的忌锯,那么今天就和各位小伙伴分享一下我對 Iterator 和 Iterable 的理解。
2.Iterator 和 Iterable
定義
在聊 Iterator 和 Iterable 之前领炫,我們得先明白二者的定義偶垮,俗話說“繁瑣問題必有猥瑣解法”,我們用一個“猥瑣”的方式來理解二者的含義
Iterable:英語好的小伙伴應(yīng)該都知道帝洪,以 able 結(jié)尾的單詞似舵,表示的含義都是【可以…】或【支持…】,那么 Iterable 所代表的含義也就是可迭代的葱峡、支持迭代的砚哗,因此我們可以知道,實現(xiàn)了 Iterable 接口的對象是支持迭代砰奕,是可迭代的蛛芥。
Iterator:在英語中以 or 結(jié)尾的單詞表示的含義是【 …樣的人】或【 …者】,就像 creator 表示的是創(chuàng)作者的意思军援。那么這里也是一樣的仅淑,iterator 就是迭代者,我們一般叫迭代器胸哥,它就是提供迭代機制的對象涯竟,具體如何迭代,都是 Iterator 接口來規(guī)范的。
P.S. 在 Java 設(shè)計模式中也有一種模式叫迭代器模式昆禽,Iterator 就是迭代器模式的一個應(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 接口中有三個方法蝗蛙,分別是:
Iterator iterator() :該接口主要是用來返回T類型的元素上的一個迭代器。
default void forEach(Consumer action) :該方法是循環(huán)輸出醉鳖,對內(nèi)部元素進行遍歷并對元素進行指定的操作捡硅。
default Spliterator spliterator():該方法提供了一個可以并行遍歷元素的迭代器,以適應(yīng)現(xiàn)在cpu多核時代并行遍歷的需求盗棵。
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 中包含了四個方法,分別是:
boolean hasNext():判斷需要遍歷的集合是否存在下一個元素纹因,即如果被迭代遍歷的集合還沒有被遍歷完喷屋,返回 true
E next():返回集合里面的下一個元素
default void remove():刪除集合里面上一次 next() 方法返回的元素
forEachRemaining(Consumer action):該方法是JDK 1.8后新增默認方法,含義是使用Lambda表達式來遍歷集合元素
看到這可能有些小伙伴就會問了:default void forEachRemaining(Consumer action) 方法怎么用呢瞭恰?它和 forEach() 方法又有什么區(qū)別呢屯曹?我們接著往下看~
forEachRemaining 方法是使用就很簡單了,直接給大家貼一個示例代碼
public static void main(String[] args) {
List list = new ArrayList<String>();
list.add("我在");
list.add("人民廣場");
list.add("吃著炸雞");
list.iterator().forEachRemaining(str-> System.out.println(str));
}
關(guān)于二者的區(qū)別也很簡單惊畏,我們通過源碼就可以一目了然~
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);
}
}
這兩個方法說是跟孫悟空和六耳獼猴一樣恶耽,二者長得太像了,他們都可以遍歷集合而且都是接口的默認方法颜启,唯一不同的地方就是循環(huán)方式不一樣偷俭。前者內(nèi)部是通過使用迭代器來遍歷的所有元素;而后者內(nèi)部使用的是增強 for 循環(huán)遍歷的元素缰盏,不同的遍歷方式也導(dǎo)致了 forEach() 方法可以多次調(diào)用涌萤,而 forEachRemaining() 方法在第二次調(diào)用時不會做任何操作,因為不會有下一個元素了口猜。
Iterator 和 Iterable 的區(qū)別
通過上面的講解负溪,相信各位小伙伴對 Iterator 和 Iterable 有了一些自己的見解,最后我們再一起總結(jié)一下二者之間的區(qū)別济炎。
Iterator 是迭代器類川抡,而 Iterable 是一個接口,約束某類是否可迭代冻辩,某個類只要實現(xiàn)了 Iterable 接口就可以使用 foreach 進行迭代猖腕。同時 Iterable 中又封裝了 Iterator 接口迅耘,只要實現(xiàn)了 Iterable 接口的類混驰,也就可以使用 Iterator 迭代器了年扩。集合Collection蚤认、List贱呐、Set都是 Iterable 的實現(xiàn)類关面,所以他們及其他們的子類都可以使用 foreach 進行迭代蜕青。
上面我們提到了 Collection瘟栖、List、Set 都是 Iterable 的實現(xiàn)類蜡豹,那為什么一定要去實現(xiàn) Iterable 這個接口呢麸粮?直接實現(xiàn) Iterator 接口不行嗎?答案肯定是不行的镜廉,原因也很簡單
Iterator 接口中的核心方法 next() 或 hasNext() 是依賴于迭代器的當前迭代位置的弄诲。如果Collection直接實現(xiàn)Iterator接口,則導(dǎo)致集合對象中包含當前迭代位置的數(shù)據(jù)(可以理解成指針)娇唯。當集合在不同方法間被傳遞時齐遵,由于當前迭代位置不可預(yù)知,那么 next() 方法的結(jié)果也就成了一個未知數(shù)塔插;而 Iterable 則不然梗摇,每次調(diào)用都會返回一個從頭開始計數(shù)的迭代器,并且多個迭代器是互不干擾的想许,所以 Collection伶授、List、Set 集合實現(xiàn)的是 Iterable 而非 Iterator 流纹。
總結(jié)
經(jīng)驗有限糜烹,有些地方可能講的沒有特別到位,如果文章中有錯誤捧颅,歡迎大家留言指正景图;若您有更好较雕、更獨到的理解碉哑,歡迎您留下寶貴想法。