关于Scala中Iterator中size/length的一个坑
Scala
语言是出了名的语法诡异,功能强大-_-,他的Iterator
也是如此,还提供了亲民心的size/length
方法,但是,但是,但是慎用!!
事情是这样的
今天在做这个:进行采样,在mapPartitions中操作,会传来一个Iterator迭代器,里面存着原始数据,我需要做的大概是先统计迭代器中的数量N(使用size方法来做),然后计算出一个需要采样的量n,然后遍历(直接for来做)这个迭代器,按自己的业务采样n个记录!
清晰明了的一个程序,这尼玛最终采样的变量一直是空,一直是空,一直是空。
起初还以为是概率那块算错了,导致采不出来,但是单独把程序剥离出来debuge
发现这个for
压根没数据,但是明明这个迭代器的里面的size
是有值的啊,奇了怪了。。。
后来发现问题就是处在这个size
方法。。
Iterator
大致是满足如下继承关系的:AbstractIterator->Iterator->TraversableOnce->GenTraversableOnce
在TraversableOnce
这个trait
中对size
方法进行了实现,
现在来看源码:
1 | def size: Int = { |
在Iterator
这个trait
中对length
方法进行了实现,1
2
3
4
5
6/** Returns the number of elements in this iterator.
* $willNotTerminateInf
*
* @note Reuse: $consumesIterator
*/
def length: Int = this.size
可以发现size/length
是通过遍历迭代器累加来计数的,这样就是导致使用了size/length
之后就无法再取迭代器里面的值,然后就出现了上述for
根本无法进入。。-_-||
然后就会出现这样的测试结果1
2
3val it=List(1,2,3,4).iterator
System.out.println(it.size);
System.out.println(it.size);
4
0
这是因为collection.iterator
会产生一个新的迭代器实例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18override /*IterableLike*/
def iterator: Iterator[A] = new AbstractIterator[A] {
var these = self
def hasNext: Boolean = !these.isEmpty
def next(): A =
if (hasNext) {
val result = these.head; these = these.tail; result
} else Iterator.empty.next
/** Have to clear `these` so the iterator is exhausted like
* it would be without the optimization.
*/
override def toList: List[A] = {
val xs = these.toList
these = newBuilder.result
xs
}
}
第一次size
完了之后就已经将该迭代器遍历掉了-_-
个人感觉
Iterator.size
这个方法简直是一个坑,还不如像java
一样不提供该方法,所以慎用慎用..
本作品采用[知识共享署名-非商业性使用-相同方式共享 2.5]中国大陆许可协议进行许可,我的博客欢迎复制共享,但在同时,希望保留我的署名权kubiCode,并且,不得用于商业用途。如您有任何疑问或者授权方面的协商,请给我留言。