文章目录

Scala语言是出了名的语法诡异,功能强大-_-,他的Iterator也是如此,还提供了亲民心的size/length方法,但是,但是,但是慎用!!

事情是这样的

今天在做这个:进行采样,在mapPartitions中操作,会传来一个Iterator迭代器,里面存着原始数据,我需要做的大概是先统计迭代器中的数量N(使用size方法来做),然后计算出一个需要采样的量n,然后遍历(直接for来做)这个迭代器,按自己的业务采样n个记录!

清晰明了的一个程序,这尼玛最终采样的变量一直是空,一直是空,一直是空。
起初还以为是概率那块算错了,导致采不出来,但是单独把程序剥离出来debuge发现这个for压根没数据,但是明明这个迭代器的里面的size是有值的啊,奇了怪了。。。

后来发现问题就是处在这个size方法。。

Iterator大致是满足如下继承关系的:
AbstractIterator->Iterator->TraversableOnce->GenTraversableOnce

TraversableOnce这个trait中对size方法进行了实现,
现在来看源码:

1
2
3
4
5
def size: Int = {
var result = 0
for (x <- self) result += 1 //额,它的计数是直接遍历得来的..
result
}

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
3
val 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
18
override /*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,并且,不得用于商业用途。如您有任何疑问或者授权方面的协商,请给我留言

文章目录