当我使用Spark mapPatartitions时遇到了一些奇怪的东西,创建的mutable.HashSet无法在map进程中正确填充,这里是代码:
object Test {
def main(args: Array[String]) {
val conf = new SparkConf().setAppName("Test").setMaster("local")
val sc = new SparkContext(conf)
val input = List[String]("1", "2", "3", "3", "4", "5", "5")
val result = sc.parallelize(input)
.mapPartitions((pi: Iterator[String]) => {
val valuesInPartition = new mutable.HashSet[String]()
val values = pi.map(line => {
valuesInPartition.add(line)
println("processing line: " + line + ", valuesInPartition: " + valuesInPartition)
})
println("valuesInPartition: " + valuesInPartition)
values
})
result.collect
}
}
和输出:
valuesInPartition: Set()
processing line: 1, valuesInPartition: Set(1)
processing line: 2, valuesInPartition: Set(1, 2)
processing line: 3, valuesInPartition: Set(3, 1, 2)
processing line: 3, valuesInPartition: Set(3, 1, 2)
processing line: 4, valuesInPartition: Set(3, 4, 1, 2)
processing line: 5, valuesInPartition: Set(3, 4, 1, 5, 2)
processing line: 5, valuesInPartition: Set(3, 4, 1, 5, 2)
但是据我所知,mapPartition中的代码应该按顺序执行,它应该在" map"之后打印最后一行。功能完成。但是这里的Set打印出来没有填充值。
我想我在这里理解错了,请帮我指出。
答案 0 :(得分:4)
这与Spark无关 - 误解是关于Iterator
和map
方法的语义。请记住,Iterator
是一种一次遍历一个元素的方法。调用pi.map(line => ...)
会产生另一个Iterator
- 但只有在请求该元素时才会感觉到生成Iterator
的每个元素所涉及的副作用。
考虑以下(普通的旧Scala)REPL交互:
scala> val l1 = List(1,2,3,4,5)
l1: List[Int] = List(1, 2, 3, 4, 5)
scala> val l2 = l1.map(println)
1
2
3
4
5
l2: List[Unit] = List((), (), (), (), ())
scala> val i1 = Iterator(1,2,3,4,5)
i1: Iterator[Int] = non-empty iterator
scala> val i2 = i1.map(println) // Look Ma, nothing happened!!
i2: Iterator[Unit] = non-empty iterator
scala> i2.next // Request the first element...
1
scala> i2.next // Request the second element...
2
scala> val l3 = i2.toList // Request remaining elements.
3
4
5
l3: List[Unit] = List((), (), ())
在您的情况下,Iterator
中存储的values
仅在退出匿名函数后遍历(因此在println("valuesInPartition: " + valuesInPartition)
之后)。