Scala中Iterator允許執行一次


背景

  使用spark執行mapPartitionsWithIndex((index,iterator)=>{....}),在執行體中將iterator進行一次迭代后,再次根據iterator執行迭代,iterator迭代體未執行。

猜想及驗證過程

  猜測iterator只能執行一次迭代。

  測試例子如下:

val rdd1 = sc.makeRDD(1 to 10,2)
val rdd2 = rdd1.mapPartitionsWithIndex{(index,iterator)=>{
	var result = List[String]()
	var sum = 0
	var count = 0
	while(iterator.hasNext){
		sum += iterator.next()
	}
	while(iterator.hasNext){
		count += 1
	}
	result.::(index + "|" + sum + "|" + count).iterator
}}

執行結果
res0: Array[String] = Array(0|15|0, 1|40|0)

   通過執行結果可以看出sum執行了求和運算,count沒有執行統計數量運算或未正確執行統計數量運算,推測可能的原因:1. iterator能夠重復執行迭代,但是count的算術運算出現問題;2.iterator只能執行一次迭代;

  對原因1的驗證例子:

val rdd1 = sc.makeRDD(1 to 10,2)
val rdd2 = rdd1.mapPartitionsWithIndex{(index,iterator)=>{
	var result = List[String]()
	var sum = 0
	var count = 0
	while(iterator.hasNext){
		sum += iterator.next()
                count += 1
	}
	result.::(index + "|" + sum + "|" + count).iterator
}}

執行結果
res0: Array[String] = Array(0|15|5, 1|40|5)

  如果iterator能夠重復執行迭代,但是count的統計數量計算出現問題,那么將sum和count放在同一個迭代體中,執行結果會和在兩個迭代體中執行結果一致。但是執行結果卻是能夠正常的統計出數量,證明了推測原因1不成立。

  對原因2的驗證例子:

  為了單純的驗證是iterator執行問題,下邊的例子去掉了spark相關的函數

val iterator = Iterator(1,2,3,4,5,6,7)
var sum = 0
while(iterator.hasNext){
	sum += iterator.next
}
println("sum is " + sum)
val expression = if(iterator.isEmpty) "iterator is empty" else "iterator is not empty"
println(expression)

  如果iterator只能執行一次迭代的話,expression的結果是【iterator is empty】,真實執行結果如下

sum is 28
iterator is empty
iterator: Iterator[Int] = empty iterator
sum: Int = 28
expression: String = iterator is empty

  通過執行結果可以看出,expression的結果確實是【iterator is empty】,所以推測原因2成立。

結論

  scala中iterator只能執行一次迭代,如果需要多次執行同一個迭代體,建議調用iterator.toList等方法,將迭代體轉化為集合,再執行上述的驗證例子就會正常。

擴展

  1.iterator.min和iterator.max同樣是通過迭代獲得,所以對於同一個iterator的min和max只能獲取一個。

  2.java中Iterator類同scala的Iterator,只允許進行一次迭代,如果需要進行多次迭代,需要將iterator轉化為集合類

  3.C#中沒有Iterator類,但是有IEnumerator,這個類可以通過IEnumerator.Reset方法來重置,迭代完進行重置就可以再次迭代,而對於java和scala的Iterator沒有相似的方法;

補充

  spark的mapPartitionsWithIndex中iterator盡量不要使用toList,原因:toList相當於將迭代數據進行了緩存,容易導致OutOfMemory的異常,iterator是流式的處理,處理完一條記錄才會去讀取下一條記錄並且會丟棄已讀的記錄,無法重復使用;而iterator.toList會將所有的記錄進行緩存,便於重復使用。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM