我正在编写一个应用程序服务器,并且有一个消息发送循环。消息由字段组成,因此可以视为迭代字段的迭代器。并且有一个由消息循环处理的消息队列,但循环在任何时候都是可破坏的(例如,当套接字缓冲区已满时)并且可以在以后恢复。目前的实施如下:
private val messageQueue: Queue[Iterator[Field]]
sent = 0
breakable {
for (iterator <- messageQueue) {
for (field <- iterator) {
... breakable ...
}
sent += 1
}
} finally messageQueue.trimStart(sent)
这可以工作并且不错,但是我认为如果我可以使用使用++运算符连接迭代器的迭代器替换队列,我可以使代码更清晰一些。说:
private val messageQueue: Iterator[Field] = message1.iterator ++ message2.iterator ++ ...
breakable {
for (field <- messageQueue) {
... breakable ...
}
}
现在代码看起来更干净但是存在性能问题。连接的迭代器在内部形成(非平衡)树,因此next()操作需要O(n)的时间。因此,迭代总体上需要O(n ^ 2)的时间。
总而言之,消息只需要处理一次,因此队列不需要是Traversable。迭代器(TraversableOnce)会这样做。我想将消息队列视为连续迭代器的集合,但++存在性能问题。是否有一个很好的解决方案可以使代码更清晰但同时又高效?
答案 0 :(得分:2)
您是否考虑过使用Stream
和#:::
懒洋洋地将您的邮件连接在一起?
private val messageQueue: Stream[Field] = message1.toStream #::: message2.toStream #::: ...
breakable {
for (field <- messageQueue) {
... breakable ...
}
}
至于时间复杂度,我相信你连接的迭代器数量是O(n)(你需要为每个迭代器调用toStream
并将它们#:::
连接在一起) 。但是,单个toStream
和#:::
操作应该是O(1),因为它们是懒惰的。以下是toStream
的{{1}}实现:
Iterator
这将花费一些时间,因为Stream.cons的第二个参数是按名称调用的,因此在您实际访问尾部之前不会对其进行评估。
然而,转换为Stream 将为每个元素访问添加一个常量的开销因子,即不是仅仅在迭代器上调用def toStream: Stream[A] =
if (self.hasNext) Stream.cons(self.next, self.toStream)
else Stream.empty[A]
,而是需要做一些额外的方法调用强制流的惰性尾部并访问包含的值。
答案 1 :(得分:2)
如果你只是压扁它们怎么办?
def flattenIterator[T](l: List[Iterator[T]]): Iterator[T] = l.iterator.flatten