【问题标题】:What is the relation between Iterable and Iterator?Iterable 和 Iterator 之间有什么关系?
【发布时间】:2012-07-03 08:59:45
【问题描述】:

scala 中的IteratorIterable 有什么区别?

我认为Iterable 表示我可以迭代的集合,而Iterator 是指向可迭代集合中某个项目的“指针”。

但是,Iterator 具有 forEachmapfoldLeft 等功能。它可以通过toIterable 转换为Iterable。例如,scala.io.Source.getLines 返回 Iterator,而不是 Iterable

但我不能在Iterator 上做groupBy,我可以在Iterable 上做。

那么,IteratorIterable 这两者之间是什么关系?

【问题讨论】:

    标签: scala iterator


    【解决方案1】:

    简而言之:Iterator 确实有状态,而 Iterable 没有。

    请参阅两者的 API 文档。

    Iterable:

    可迭代集合的基本特征。

    这是定义迭代器的所有 Scala 集合的基本特征 方法来逐个遍历集合的元素。 [...] 这个 trait 通过步进实现了 Iterable 的 foreach 方法 使用迭代器遍历所有元素。

    Iterator:

    迭代器是允许迭代一个序列的数据结构 元素。他们有一个 hasNext 方法来检查是否有下一个 元素可用,以及返回下一个元素的 next 方法 并从迭代器中丢弃它。

    迭代器是可变的:对它的大多数操作都会改变它的状态。尽管 它通常用于遍历集合的元素,它 也可以在没有任何集合支持的情况下使用(参见 伴随对象上的构造函数)。

    使用Iterator,您可以停止迭代并在以后继续进行。如果您尝试使用Iterable 执行此操作,它将再次从头部开始:

    scala> val iterable: Iterable[Int] = 1 to 4
    iterable: Iterable[Int] = Range(1, 2, 3, 4)
    
    scala> iterable.take(2)
    res8: Iterable[Int] = Range(1, 2)
    
    scala> iterable.take(2)
    res9: Iterable[Int] = Range(1, 2)
    
    scala> val iterator = iterable.iterator
    iterator: Iterator[Int] = non-empty iterator
    
    scala> if (iterator.hasNext) iterator.next
    res23: AnyVal = 1
    
    scala> if (iterator.hasNext) iterator.next
    res24: AnyVal = 2
    
    scala> if (iterator.hasNext) iterator.next
    res25: AnyVal = 3
    
    scala> if (iterator.hasNext) iterator.next
    res26: AnyVal = 4
    
    scala> if (iterator.hasNext) iterator.next
    res27: AnyVal = ()
    

    请注意,我没有在 Iterator 上使用 take。这样做的原因是它使用起来很棘手。 hasNextnext 是仅有的两种保证在 Iterator 上按预期工作的方法。再次查看Scaladoc

    特别重要的是要注意,除非另有说明, 在对迭代器调用方法后,永远不要使用迭代器。他们俩 最重要的例外也是唯一的抽象方法:next 和 有下一个。

    这两种方法都可以被调用任意次数而不必 丢弃迭代器。请注意,即使 hasNext 也可能导致突变—— 例如从输入流迭代时,它将阻塞直到 流已关闭或某些输入可用。

    考虑这个安全和不安全使用的例子:

    def f[A](it: Iterator[A]) = {
      if (it.hasNext) {            // Safe to reuse "it" after "hasNext"
        it.next                    // Safe to reuse "it" after "next"
        val remainder = it.drop(2) // it is *not* safe to use "it" again after this line!
        remainder.take(2)          // it is *not* safe to use "remainder" after this line!
      } else it
    }
    

    【讨论】:

    • Odersky 和 ​​Spoon 写了一本很好的 Scala 集合类入门:见 scala-lang.org/docu/files/collections-api/collections.html
    • 我在 Scala 2.11.7 中测试过,iterator 的行为类似于 iterable,即当你第二次调用take(2) 时,你仍然得到List(1, 2)
    • @qed 感谢您的评论,我的回答部分不正确。你不能在迭代器上多次调用take。我编辑了我的答案并提到了这一点。
    • “在调用方法后永远不应该使用迭代器”实际上是什么意思?一个迭代器上的方法应该只调用一次吗?在这种情况下,迭代器将毫无用处。
    • @qed 您可以随时调用hasNextnext,只要hasNext 返回true。所有其他方法只能调用一次并返回一个新的迭代器。在这个新引入的迭代器上,可以调用另一个方法,但不能在第一个迭代器上调用。我认为这不是迭代器应该如何工作的最佳设计,但我不知道是什么导致了这些设计决策。
    【解决方案2】:

    Martin Odersky 和 ​​Lex Spoon 的另一个解释:

    foreach 方法之间有一个重要的区别 迭代器和可遍历集合上的相同方法:调用时 对于迭代器,foreach 将在迭代器结束时离开 完毕。所以在同一个迭代器上再次调用 next 将失败并返回 没有此类元素异常。相比之下,当在集合上调用时, foreach 保持集合中元素的数量不变 (除非传递的函数添加到删除元素,但这是 气馁,因为它可能会导致令人惊讶的结果)。

    来源:http://www.scala-lang.org/docu/files/collections-api/collections_43.html

    还要注意(感谢 Wei-Ching Lin 的提示)Iterator 扩展了 TraversableOnce 特征,而 Iterable 没有。

    【讨论】:

      猜你喜欢
      • 2016-08-20
      • 2016-06-27
      • 2013-10-30
      • 2015-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-08
      • 2017-09-15
      相关资源
      最近更新 更多