假设我有一个懒惰的迭代器[Item]。只有当我们迭代迭代器时,才会懒惰地创建items对象。这些物品的制作成本很高。
我想将此迭代器序列化为JSON数组。它有效(使用Jackson scala模块),但在我看来效率不高。
据我了解,它目前的工作原理如下:
我希望项目的计算和项目的序列化并行发生。
我想要一个Iterator,它会在阅读下一个项目时开始计算一定数量的下一个项目。
例如我喜欢在执行iterator.next()时,在场景后面,接下来的50个项目得到计算,而不会阻塞迭代线程(它应该只等待下一个元素可用)。
我已经看过“BufferedIterator”,但它并不是我需要的,因为我并不想明确地查询“head”,而且我需要超过1个项目来预加载
有关如何实现这一目标的任何想法吗?
我也可以使用Stream替换Iterator的解决方案,但由于内存使用率较低而优先选择Iterator
答案 0 :(得分:1)
如果我理解你的问题,那么这就是你可以做的一个例子。您可以将每个项目计算包装在Future
中,这样您就可以在不阻塞的情况下迭代输入流,并在每个块准备好后对其进行处理/序列化。我将在REPL中执行此操作并在评估每件作品时进行打印,这样您就可以看到每件事情发生的时间:
@ import concurrent._, ExecutionContext.Implicits.global
import concurrent._, ExecutionContext.Implicits.global
@ def futureItem(i: Int): Future[Int] = Future {
Thread.sleep(1000)
println(s"item: ${i}")
i
}
defined function futureItem
@ val inputIterator = (1 to 9).toIterator.map(futureItem)
inputIterator: Iterator[Future[Int]] = non-empty iterator
因此,计算每个项目至少需要1秒钟。现在我们想要以块的形式处理项目,这也需要一些时间:
@ def computeItemsChunk(items: Seq[Int]): Int = {
Thread.sleep(1000)
val s = items.sum
println(s"chunk ${items}: ${s}")
s
}
defined function computeItemsChunk
现在我们将输入流分组,应用Future.sequence
并计算块:
@ case object foo {
val chunksIterator = inputIterator.grouped(3).map { futureItems =>
Future.sequence(futureItems).map(computeItemsChunk)
}
}
defined object foo
(我在一个对象中定义它,因为否则,分组(或其他)将强制评估第一个块)。现在让我们看看它是如何评估的:
@ Await.result(Future.sequence(foo.chunksIterator), Duration.Inf)
item: 2
item: 3
item: 4
item: 1
item: 7
item: 6
chunk List(1, 2, 3): 6
item: 5
item: 8
chunk List(4, 5, 6): 15
item: 9
chunk List(7, 8, 9): 24
res5: Iterator[Int] = non-empty iterator
您可以看到,一旦项目可用就会计算块,并且迭代器会在不等待每个块评估的情况下前进。