背景java
使用spark執行mapPartitionsWithIndex((index,iterator)=>{....}),在執行體中將iterator進行一次迭代後,再次根據iterator執行迭代,iterator迭代體未執行。express
猜測及驗證過程緩存
猜想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只能執行一次迭代;spa
對緣由1的驗證例子:scala
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不成立。blog
對緣由2的驗證例子:it
爲了單純的驗證是iterator執行問題,下邊的例子去掉了spark相關的函數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會將全部的記錄進行緩存,便於重複使用。