我理解map
和&之间的基本区别foreach
(懒惰和渴望),我也理解为什么这段代码片段
sc.makeRDD(Seq("a", "b")).map(s => new java.io.ByteArrayInputStream(s.getBytes)).collect
应该给出
java.io.NotSerializableException:java.io.ByteArrayInputStream
然后我认为应该使用以下代码片段
sc.makeRDD(Seq("a", "b")).foreach(s => {
val is = new java.io.ByteArrayInputStream(s.getBytes)
println("is = " + is)
})
但是这段代码运行良好。为什么这样?
答案 0 :(得分:2)
实际上map
和foreach
之间的根本区别不在于评估策略。让我们来看看签名(为简洁起见,我省略了map
的隐式部分):
def map[U](f: (T) ⇒ U): RDD[U]
def foreach(f: (T) ⇒ Unit): Unit
map
从T
到U
的函数将其应用于现有RDD[T]
的每个元素并返回RDD[U]
。允许像sh U
一样的操作必须是可序列化的。
foreach
从T
到Unit
(类似于Java void
)采用函数,并且本身不返回任何内容。一切都在本地发生,没有涉及网络流量,因此不需要序列化。与map
不同,foreach
应该在想要获得某种副作用时使用,例如your previous question。
另一方面,这两个实际上是不同的。您在map
中使用的匿名函数是一个函数:
(s: String) => java.io.ByteArrayInputStream
和你在foreach
中使用的那个:
(s: String) => Unit
如果您使用map
的第二个函数,您的代码将编译,但结果将远离您想要的结果(RDD[Unit]
)。
答案 1 :(得分:0)
collect
呼叫。
以下是我在spark-shell中进行测试的结果。
下面通过,因为没有数据必须发送到其他节点。
sc.makeRDD(1 to 1000, 1).map(_ => {NullWritable.get}).count
以下调用失败,因为地图输出可以发送到其他节点。
sc.makeRDD(1 to 1000, 1).map(_ => {NullWritable.get}).first
sc.makeRDD(1 to 1000, 1).map(_ => {NullWritable.get}).collect
重新分区会强制将数据分发到节点,这会失败。
sc.makeRDD(1 to 1000, 1).map(_ => {NullWritable.get}).repartition(2).saveAsTextFile("/tmp/NWRepart")
呼叫传递下方没有分区。
sc.makeRDD(1 to 1000, 1).map(_ => {NullWritable.get}).saveAsTextFile("/tmp/NW")