关于Scala迭代器中dropWhile谓词使用的疑问
为什么Scala Iterator调用dropWhile后next返回"of"而非"number"?
这个问题的核心在于Scala Iterator的有状态特性,再加上Scala REPL的隐式行为,咱们一步步理清楚:
首先得明白Scala Iterator的本质
和List、Array这类不可变集合不同,Scala的Iterator是一个单遍、有状态的指针——它只能遍历一次,所有操作(包括dropWhile)都是惰性执行的,只有当你真正尝试获取元素(比如调用next()或hasNext())时,才会实际执行逻辑。而且所有基于同一个原Iterator的操作都会共享这个指针,一旦指针前进就没法后退。
你的操作流程到底发生了什么
我们来逐行拆解你在REPL里的操作:
1. 创建迭代器
scala> val it = Iterator("a" , "number" , "of" , "words") it: Iterator[String] = non-empty iterator
此时迭代器it的指针指向第一个元素"a",还没开始遍历。
2. 调用dropWhile但没保存返回值
scala> it dropWhile ( _.length < 2 ) res52: Iterator[String] = non-empty iterator
这里藏着关键的细节:
dropWhile本身不会立刻修改原迭代器的指针,它返回一个包装后的新迭代器,这个迭代器会记住要跳过的条件。- 但Scala REPL在处理这个返回值时,为了显示
res52的内容,会调用该迭代器的toString()方法。而Iterator的toString()会调用hasNext()来判断是否为空——这就触发了dropWhile的实际执行!
触发后,dropWhile开始工作:
- 检查当前指针的元素
"a",满足length < 2,跳过,指针移动到"number"。 - 检查
"number",长度6不满足条件,dropWhile停止,此时hasNext()返回true,所以REPL显示non-empty iterator。
到这里,原迭代器it的指针已经停在"number"的位置了。
3. 为什么调用it.next()得到"of"?
按道理这时候调用it.next()应该返回"number",然后指针移动到"of"。你得到"of"的原因,大概率是在REPL中不小心触发了额外的遍历操作(比如误碰回车重复执行,或者REPL的额外输出逻辑调用了next())。但不管怎样,核心问题是你不应该混合使用原迭代器和dropWhile返回的迭代器。
正确的用法
如果你想获取跳过前缀后的元素,应该保存dropWhile返回的迭代器,用它来遍历:
scala> val it = Iterator("a" , "number" , "of" , "words") it: Iterator[String] = non-empty iterator scala> val filteredIt = it dropWhile ( _.length < 2 ) filteredIt: Iterator[String] = non-empty iterator scala> filteredIt next res0: String = number // 这就是你预期的结果
这样就能保证你从第一个不满足条件的元素开始遍历,不会因为原迭代器的状态变化导致混乱。
内容的提问来源于stack exchange,提问作者George Curington
相关产品推荐
相关产品推荐

