Scala - Iterator的懒惰和Iterable - 内存消耗

时间:2013-12-13 10:27:44

标签: scala iterator iterable

好的,我正在使用dbpedia处理英语维基百科转储。到目前为止,他们的实现扩展了Traversable并提供了foreach来覆盖转储。但是,我希望有mapgrouped等典型的地图操作。以下是我打开的问题:https://github.com/dbpedia/extraction-framework/issues/140

所以我添加了一个getter来接收一个iterable和一个迭代器。现在有趣的部分:

source.iterable
      .map(parser)
      .zipWithIndex
      .map { case(page: PageMode, i: Int) =>
                 if(i%1000 == 0){println(i)}
                 (...)
            }
      .grouped(2000)

上面的代码内存不足。但是:

source.iterator
      .map(parser)
      .zipWithIndex
      .map { case(page: PageNode, i: Int) =>
                 if(i%1000 == 0){println(i)}
                 (...)
            }
      .grouped(2000)

此代码会立即返回。

在我看来,第一个示例在内存耗尽时完全运行代码,因为它试图将转储存储在内存中。后者没有。但是,后者在Seq上返回迭代器而不是在迭代器上返回迭代器。

这是一个可迭代类的预期还是我做错了什么。我希望它们都能立即返回并且只在迭代后消耗内存。

谢谢你的帮助! 卡斯滕

2 个答案:

答案 0 :(得分:3)

默认情况下,Scala中的所有集合(流和视图除外)都是严格的,因此每个函数都集合在一起:

pages
  .map(parser)
  .zipWithIndex
  .map { partialFunction }

将返回新集合。您可以使用视图避免某些中间结果,然后将其强制恢复为您的集合类型:

pages.view
  .map(parser)
  .zipWithIndex
  .map { partialFunction }
  .force

了解更多详情http://www.scala-lang.org/docu/files/collections-api/collections_42.html

答案 1 :(得分:0)

调用iterable会返回Iterable,这只是指具有iterator方法的集合。所以:

  • source.iterable返回一个可迭代的集合,可能会也可能不会完全保留在内存中
  • 但后来mapzipWithIndexmapgrouped都生成了中间集合

另一方面呼叫iterator

  • source.iterator会针对可能会或可能不会完全记忆的内容返回Iterator
  • 然后mapzipWithIndexmapgrouped将不会创建中间集合(它们会创建新的迭代器)

在我看来,这解释了为什么第一个例子更容易耗尽内存。