3

作为一名 C# 程序员,我对 Java / Scala 迭代器设计有一个粗略的了解。

我正在尝试(懒惰地-因为来源可能很大)从RecordReader(在某些第三方库中)读取记录。我需要每 100 条记录做一些额外的工作。

for (group <- reader.iterator.zipWithIndex.grouped(100)) {
  for ((record, i) <- group) {
    println(i + "|" + record.key)
  }
  // ...
}

这给了我最后的记录,重复,每次。

如果我不使用grouped,它可以正常工作并且我得到每条记录。我是否缺少有关延迟流或 Java 迭代器的信息?

4

2 回答 2

3

要进行故障排除,请尝试在另一个迭代器中装饰您的迭代器,以打印正在发生的事情:

def wrap[T](i: Iterator[T]) = new Iterator[T] {
  def hasNext = { val b = i.hasNext; println("hasNext => " + b); b }
  def next() = { val n = i.next(); println("next() => " + n); n }
}

val reader = Iterator.from(20).take(10).toList
for (group <- wrap(reader.iterator).zipWithIndex.grouped(5)) {
  for ((v, i) <- group) println("[" + i + "] = " + v)
}

在迭代器上调用 wrap,第一次实例化迭代器。这将打印如下内容:

hasNext => true
hasNext => true
next() => 20
hasNext => true
next() => 21
hasNext => true

这应该可以帮助您确定迭代器是否表现不佳......例如,库可能无法正确处理hasNext多次调用而不调用next. 在这种情况下,您可以进行修改wrap以使迭代器行为正确。还有一件事,从症状来看,感觉就像您在调用分组之前已经使用了迭代器。所以要格外小心,检查你之前是否使用过相同的迭代器引用。

于 2012-10-24T14:05:34.557 回答
3

我认为问题可能是Record.key只返回一些变量的当前值,这些变量在迭代器被消耗时发生了变异(而不是让记录在构造时实际捕获键值)。一个例子可能会使它更清楚。首先,让我们使用 scala REPL 编写一些没有出现问题的测试代码:

case class Record( key: Int )
def getRecordIterator: Iterator[Record] = {
  var currentKey: Int = 0
  (1 to 10).iterator.map{ i => 
    currentKey += 1
    new Record( currentKey )
  }
}

然后我们可以尝试在不使用的情况下进行迭代grouped

for ((record, i) <- getRecordIterator.zipWithIndex) {
  println(i + "|" + record)
}

这给了我们(如预期的那样)

0|Record(1)
1|Record(2)
2|Record(3)
3|Record(4)
4|Record(5)
5|Record(6)
6|Record(7)
7|Record(8)
8|Record(9)
9|Record(10)

然后使用grouped

for (group <- getRecordIterator.zipWithIndex.grouped(3)) {
  for ((record, i) <- group) {
    println(i + "|" + record)
  }
  println("---")
}

这使:

0|Record(1)
1|Record(2)
2|Record(3)
---
3|Record(4)
4|Record(5)
5|Record(6)
---
6|Record(7)
7|Record(8)
8|Record(9)
---
9|Record(10)
---    

到现在为止,一切都很好。

Record现在让我们稍微改变一下定义:

trait Record {
  def key: Int
  override def toString = "Record(" + key + ")"
}
def getRecordIterator: Iterator[Record] = {
  var currentKey: Int = 0
  (1 to 10).iterator.map{ i => 
    currentKey += 1
    new Record{ def key = currentKey }
  }    
}

有了这个改变,我们在不使用时仍然有相同的结果grouped,但这是我们在使用时得到的结果group

0|Record(3)
1|Record(3)
2|Record(3)
---
3|Record(6)
4|Record(6)
5|Record(6)
---
6|Record(9)
7|Record(9)
8|Record(9)
---
9|Record(10)
---

问题的根源在于,仅调用next我们的迭代器就会改变Record.get. 这个问题可以更简单地说明:

val it = getRecordIterator
val r1 = it.next
println(r1) // prints "Record(1)" as expected
val r2 = it.next
println(r2) // prints "Record(2)" as expected
println(r1) // this now prints "Record(2)", not "Record(1)" anymore!
于 2012-10-24T14:15:11.340 回答