字节面试官问:Iterator和erable有什么区别?

1.前言
在面试的时候,如果面试官问你:Iterator 和 Iterable 有什么区别,你会怎么回答这个问题呢?要是一年前的我来回答这个问题的话,估计我直接就如鲠在喉、哑口无言了,不过现在的我还是能跟面试官聊上几句的,那么今天就和各位小伙伴分享一下我对 Iterator 和 Iterable 的理解。

2.Iterator 和 Iterable
定义
在聊 Iterator 和 Iterable 之前,我们得先明白二者的定义,俗话说“繁琐问题必有猥琐解法”,我们用一个“猥琐”的方式来理解二者的含义

Iterable:英语好的小伙伴应该都知道,以 able 结尾的单词,表示的含义都是【可以…】或【支持…】,那么 Iterable 所代表的含义也就是可迭代的、支持迭代的,因此我们可以知道,实现了 Iterable 接口的对象是支持迭代,是可迭代的。

Iterator:在英语中以 or 结尾的单词表示的含义是【 …样的人】或【 …者】,就像 creator 表示的是创作者的意思。那么这里也是一样的,iterator 就是迭代者,我们一般叫迭代器,它就是提供迭代机制的对象,具体如何迭代,都是 Iterator 接口来规范的。

P.S. 在 Java 设计模式中也有一种模式叫迭代器模式,Iterator 就是迭代器模式的一个应用例子

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) :该方法是循环输出,对内部元素进行遍历并对元素进行指定的操作。

default Spliterator spliterator():该方法提供了一个可以并行遍历元素的迭代器,以适应现在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() 方法又有什么区别呢?我们接着往下看~

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));
}
关于二者的区别也很简单,我们通过源码就可以一目了然~

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);
}
}
这两个方法说是跟孙悟空和六耳猕猴一样,二者长得太像了,他们都可以遍历集合而且都是接口的默认方法,唯一不同的地方就是循环方式不一样。前者内部是通过使用迭代器来遍历的所有元素;而后者内部使用的是增强 for 循环遍历的元素,不同的遍历方式也导致了 forEach() 方法可以多次调用,而 forEachRemaining() 方法在第二次调用时不会做任何操作,因为不会有下一个元素了。

Iterator 和 Iterable 的区别

通过上面的讲解,相信各位小伙伴对 Iterator 和 Iterable 有了一些自己的见解,最后我们再一起总结一下二者之间的区别。

Iterator 是迭代器类,而 Iterable 是一个接口,约束某类是否可迭代,某个类只要实现了 Iterable 接口就可以使用 foreach 进行迭代。同时 Iterable 中又封装了 Iterator 接口,只要实现了 Iterable 接口的类,也就可以使用 Iterator 迭代器了。集合Collection、List、Set都是 Iterable 的实现类,所以他们及其他们的子类都可以使用 foreach 进行迭代。

上面我们提到了 Collection、List、Set 都是 Iterable 的实现类,那为什么一定要去实现 Iterable 这个接口呢?直接实现 Iterator 接口不行吗?答案肯定是不行的,原因也很简单

Iterator 接口中的核心方法 next() 或 hasNext() 是依赖于迭代器的当前迭代位置的。如果Collection直接实现Iterator接口,则导致集合对象中包含当前迭代位置的数据(可以理解成指针)。当集合在不同方法间被传递时,由于当前迭代位置不可预知,那么 next() 方法的结果也就成了一个未知数;而 Iterable 则不然,每次调用都会返回一个从头开始计数的迭代器,并且多个迭代器是互不干扰的,所以 Collection、List、Set 集合实现的是 Iterable 而非 Iterator 。

总结
经验有限,有些地方可能讲的没有特别到位,如果文章中有错误,欢迎大家留言指正;若您有更好、更独到的理解,欢迎您留下宝贵想法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容