如何在scala中使用迭代器来有效地根据长度内存做出决策

时间:2016-06-01 01:54:55

标签: scala iterator

我有一个很长的集合,我需要在scala中迭代,我想避免将它全部保存在内存中。我想出的解决方案就是:

(rows是我正在尝试处理的迭代器,COMPONENT_LIMIT是我计算的可以保留在内存中的对象的估计值)

val ( processItr, countItr ) = rows.duplicate
val pastLimitItr = countItr.drop( COMPONENT_LIMIT )
if ( pastLimitItr.hasNext ) 
  new CustomIterator( processItr.buffered) 
else
  Iterator( MappperToObject.createObject(
            processItr.toList
          ) )

我遇到的问题是:即使我不再需要使用pastLimitItr,据我所知scala source on def duplicate,队列会挂起,所以使用的内存相对于长度迭代器。

问题是:在完成测试后,如何在def副本中删除Partner对象中的队列?测试后我根本不需要复制品。

更新:我应该补充一点,输出迭代器对象将根据其内容包含输入迭代器中的一些对象,因此我不能按照建议使用分组。

更新:看起来span是答案中给出的选项中的正确答案。我的问题可能不够具体。

1 个答案:

答案 0 :(得分:2)

听起来你想要使用:

val segments = iterator.grouped(LIMIT)
createObject(segments.next())

虽然您确实需要duplicate,但您可以排除重复项。

您还可以使用条件重要的iterator.span

scala> val it = (1 to 10).iterator
it: Iterator[Int] = non-empty iterator

scala> var n = 0 ; val (vs, rest) = it.span { _ => n += 1; n < 3 }
n: Int = 0
vs: Iterator[Int] = non-empty iterator
rest: Iterator[Int] = unknown-if-empty iterator

scala> vs.toList
res0: List[Int] = List(1, 2)

scala> rest.toList
res1: List[Int] = List(3, 4, 5, 6, 7, 8, 9, 10)

您可以将其定义为Iterator::splitAt

scala> implicit class splitItAt[A](it: Iterator[A]) {
     | def splitAt(i: Int): (Iterator[A], Iterator[A]) = {
     |   var n = 0
     |   it.span { _ => n += 1; n <= i }
     | }}
defined class splitItAt

scala> val (is, rest) = (1 to 10).iterator.splitAt(6)
is: Iterator[Int] = non-empty iterator
rest: Iterator[Int] = unknown-if-empty iterator

scala> is.toList
res2: List[Int] = List(1, 2, 3, 4, 5, 6)

但我发现你确实想要使用前缀或剩余的迭代器。

我会写一个自定义方法。或者不要笑:

scala> val (is, rest) = (1 to 10).iterator.splitAt(6)
is: Iterator[Int] = non-empty iterator
rest: Iterator[Int] = unknown-if-empty iterator

scala> is match { case it: collection.Iterator$Leading$1 if rest.hasNext => it.finish() ; rest ; case _ => is }
res6: Iterator[Int] = unknown-if-empty iterator

scala> res6.next
res7: Int = 7

内部finish表示您可以在不缓冲前缀的情况下使用rest

你也可以欺骗grouped,并使用rest的原始迭代器:

scala> val it = (1 to 10).iterator
it: Iterator[Int] = non-empty iterator

scala> val g = it.grouped(3)
g: it.GroupedIterator[Int] = non-empty iterator

scala> val first = g.next
first: List[Int] = List(1, 2, 3)

scala> it.hasNext
res12: Boolean = true

scala> it.next
res13: Int = 4

没有内部保留的自定义方法:

scala> :pa
// Entering paste mode (ctrl-D to finish)

implicit class splitItAt[A](private val it: Iterator[A]) extends AnyVal {
  def splitAt(i: Int): (List[A], Iterator[A]) = {
    val buf = mutable.ListBuffer.empty[A]
    var n = 0
    while (it.hasNext && n < i) {
      buf += it.next()
      n += 1
    }
    (buf.toList, it)
  }
}

// Exiting paste mode, now interpreting.

defined class splitItAt

scala> val (is, rest) = (1 to 10).iterator.splitAt(20)
is: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
rest: Iterator[Int] = empty iterator

scala> val (is, rest) = (1 to 10).iterator.splitAt(6)
is: List[Int] = List(1, 2, 3, 4, 5, 6)
rest: Iterator[Int] = non-empty iterator

scala> val (is, rest) = (1 to 10).iterator.splitAt(0)
is: List[Int] = List()
rest: Iterator[Int] = non-empty iterator