Iterable vs Iterator

Posted on By ᵇᵒ

Iterable 定义

public actual interface Iterable<out T> {
    /**
     * Returns an iterator over the elements of this object.
     */
    public actual operator fun iterator(): Iterator<T>
}

Iterator 定义

public actual interface Iterator<out T> {
    /**
     * Returns the next element in the iteration.
     *
     * @throws NoSuchElementException if the iteration has no next element.
     */
    public actual operator fun next(): T

    /**
     * Returns `true` if the iteration has more elements.
     */
    public actual operator fun hasNext(): Boolean
}

kotlin 扩展函数奇技淫巧 中提到了 Iterable 和 Iterator,我们知道 Iterable 只是提供了一个 iterator() 方法并返回一个 Iterator,有没有想过为什么要多此一举包装一个 Iterable? 毕竟两者都是 interface,干嘛不直接 implement Iterator?

事实上并不是多此一举,Iterable 和 Iterator 分离的核心原因是:一个表示“可遍历的数据源”,一个表示“正在遍历的游标”。

Iterator 是有状态的、一次性的:

val it = listOf(1, 2, 3).iterator()

it.next() // 1
it.next() // 2

它内部有“当前走到哪了”的状态。走过了就走过了。

而 Iterable 本身不负责保存遍历进度,它只负责提供 Iterator。每次需要遍历时,创建一个新的 Iterator(依赖具体的实现,但绝大多数情况创建新的 Iterator 才合理)。所以:

val list = listOf(1, 2, 3)

for (x in list) {
    println(x)
}

for (x in list) {
    println(x)
}

这两次 for 都能从头开始,因为每次 for 都会调用一次 list.iterator() 拿到新的 Iterator。
如果集合本身直接就是 Iterator,问题会很多。比如嵌套遍历:

val list = listOf(1, 2, 3)

for (a in list) {
    for (b in list) {
        println("$a $b")
    }
}

这里需要两个互不干扰的遍历状态:外层一个,内层一个。
如果 list 自己就是 Iterator,内层循环会把外层的游标状态也消费掉,逻辑就乱了。

所以设计上通常是:

  • Iterable:我这个对象可以被遍历,可以提供 iterator
  • Iterator:我正在遍历,知道当前走到哪里了

可以把它理解成:
Iterable = 可重复取票的入口
Iterator = 一张正在使用的票

Kotlin 的 for (x in something) 本质上关心的是 something.iterator(),所以 Iterable 是集合、容器、数据源这类对象的抽象; Iterator 则是一次具体遍历过程的抽象。

这也是为什么 List、Set 这类集合实现的是 Iterable,而不是直接把自己做成 Iterator。

另外,Iterable 和 Iterator 的几个方法都是 operator fun,对应的是同一个语言约定:for-in 遍历协议。

for (x in iterable) {
    println(x)
}

会被编译器大致展开成:

val iterator = iterable.iterator()
while (iterator.hasNext()) {
    val x = iterator.next()
    println(x)
}

for-in 里的 in 只是一个语法约定,不同于 contains 重载操作符对应的 in。

普通判断:

if (x in collection) { ... }

等价于:

if (collection.contains(x)) { ... }

这里重载的是:

operator fun contains(x: T): Boolean