我有Iterator[Record]
,record.id
以这种方式订购:
record.id=1
record.id=1
...
record.id=1
record.id=2
record.id=2
..
record.id=2
特定ID的记录可能会发生很多次,因此我想编写一个以迭代器作为输入的函数,并以惰性方式返回Iterator[Iterator[Record]]
输出。
我能够提出以下内容,但在500K记录之后StackOverflowError
失败了:
def groupByIter[T, B](iterO: Iterator[T])(func: T => B): Iterator[Iterator[T]] = new Iterator[Iterator[T]] {
var iter = iterO
def hasNext = iter.hasNext
def next() = {
val first = iter.next()
val firstValue = func(first)
val (i1, i2) = iter.span(el => func(el) == firstValue)
iter = i2
Iterator(first) ++ i1
}
}
我做错了什么?
答案 0 :(得分:3)
麻烦的是,每次Iterator.span
调用都为trailing
迭代器创建了另一个堆叠闭包,没有任何蹦床,它很容易溢出。
实际上我不认为有一个实现,它不会记住前缀迭代器的元素,因为跟随迭代器可以在前缀被排除之前访问。
即使在.span
implementation中,Queue
定义中也有Leading
个记忆元素。
我能想象的最简单的实现是以下Stream
。
implicit class StreamChopOps[T](xs: Stream[T]) {
def chopBy[U](f: T => U): Stream[Stream[T]] = xs match {
case x #:: _ =>
def eq(e: T) = f(e) == f(x)
xs.takeWhile(eq) #:: xs.dropWhile(eq).chopBy(f)
case _ => Stream.empty
}
}
虽然它可能不是最有效的,因为它会记忆很多。但是通过适当的迭代,GC应该处理过多中间流的问题。
您可以将其用作myIterator.toStream.chopBy(f)
简单检查验证以下代码可以在没有SO的情况下运行
Iterator.fill(10000000)(Iterator(1,1,2)).flatten //1,1,2,1,1,2,...
.toStream.chopBy(identity) //(1,1),(2),(1,1),(2),...
.map(xs => xs.sum * xs.size).sum //60000000
答案 1 :(得分:0)
受到@Odomontois实施的chopBy的启发,这是我为Iterator实施的一个chopBy。当然每个批量应该适合分配的内存。它看起来不是很优雅,但似乎有效:)
implicit class IteratorChopOps[A](toChopIter: Iterator[A]) {
def chopBy[U](f: A => U) = new Iterator[Traversable[A]] {
var next_el: Option[A] = None
@tailrec
private def accum(acc: List[A]): List[A] = {
next_el = None
val new_acc = hasNext match {
case true =>
val next = toChopIter.next()
acc match {
case Nil =>
acc :+ next
case _ MatchTail t if (f(t) == f(next)) =>
acc :+ next
case _ =>
next_el = Some(next)
acc
}
case false =>
next_el = None
return acc
}
next_el match{
case Some(_) =>
new_acc
case None => accum(new_acc)
}
}
def hasNext = {
toChopIter.hasNext || next_el.isDefined
}
def next: Traversable[A] = accum(next_el.toList)
}
}
这是一个匹配尾部的提取器:
object MatchTail {
def unapply[A] (l: Traversable[A]) = Some( (l.init, l.last) )
}