您不能从可遍历对象中懒惰地获取迭代器的原因是您本质上不能。Traversable 定义了foreach
, 并且foreach
不间断地遍历所有内容。那里没有懒惰。
所以你有两个选择,都很糟糕,让它变得懒惰。
首先,您可以每次都遍历整个内容。(我打算使用 Scala 迭代器,但 Java 迭代器基本相同。)
class Terrible[A](t: Traversable[A]) extends Iterator[A] {
private var i = 0
def hasNext = i < t.size // This could be O(n)!
def next: A = {
val a = t.slice(i,i+1).head // Also could be O(n)!
i += 1
a
}
}
如果你碰巧有高效的索引切片,这没问题。如果不是,每个“下一个”将花费迭代器长度的线性O(n^2)
时间,用于遍历它的时间。但这也不一定是懒惰的;如果您坚持必须是您必须O(n^2)
在所有情况下强制执行并执行
class Terrible[A](t: Traversable[A]) extends Iterator[A] {
private var i = 0
def hasNext: Boolean = {
var j = 0
t.foreach { a =>
j += 1
if (j>i) return true
}
false
}
def next: A = {
var j = 0
t.foreach{ a =>
j += 1
if (j>i) { i += 1; return a }
}
throw new NoSuchElementException("Terribly empty")
}
}
对于通用代码来说,这显然是一个糟糕的主意。
另一种方法是使用线程并阻止 foreach 的遍历。没错,您必须对每个元素访问进行线程间通信!让我们看看它是如何工作的——我将在这里使用 Java 线程,因为 Scala 正在转换到 Akka 风格的演员(尽管任何旧演员或 Akka 演员或 Scalaz 演员或 Lift 演员或(等)将起作用)
class Horrible[A](t: Traversable[A]) extends Iterator[A] {
private val item = new java.util.concurrent.SynchronousQueue[Option[A]]()
private class Loader extends Thread {
override def run() { t.foreach{ a => item.put(Some(a)) }; item.put(None) }
}
private val loader = new Loader
loader.start
private var got: Option[A] = null
def hasNext: Boolean = {
if (got==null) { got = item.poll; hasNext }
else got.isDefined
}
def next = {
if (got==null) got = item.poll
val ans = got.get
got = null
ans
}
}
这避免了O(n^2)
灾难,但会占用一个线程并且逐个元素的访问速度极慢。我在我的机器上每秒获得大约 200 万次访问,而典型的可遍历则超过 100M。对于通用代码来说,这显然是一个可怕的想法。
所以你有它。一般来说,Traversable 并不懒惰,没有什么好的方法可以让它变得懒惰而不极大地影响性能。