(一)
因为Spark很多语法及其思想都是借鉴Scala的,所以我们先看看 Scala中map()与flatMap()函数的区别,其中顺便介绍flatten方法:
(a)
使用flatten方法把一个包含列表的列表转变为一个单列表。
- 创建列表的列表:
scala> val lol = List(List(1,2), List(3,4))
lol: List[List[Int]] = List(List(1, 2), List(3, 4))
- 在列表的列表上调用flatten方法创建一个新列表:
scala> val result = lol.flatten
result: List[Int] = List(1, 2, 3, 4)
则,flatten将包含列表的列表展平为一个结果列表。
flatten方法不仅限于列表,还可以用于其他序列 (Array、 ArrayBuffer、 Vector等):
scala> val a = Array(Array(1,3), Array(2,4))
a: Array[Array[Int]] = Array(Array(1, 3), Array(2, 4))
scala> a.flatten
res0: Array[Int] = Array(1, 3, 2, 4)
flatten方法在至少其他两种场景很有用。第一,因为字符串是字符的序列,可以把字符串的列表转变为一个字符的列表(字符串的列表--> 字符的列表):
scala> val list = List("Hello", "world")
list: List[String] = List(Hello, world)
scala> list.flatten
res1: List[Char] = List(H, e, l, l, o, w, o, r, l, d)
第二,因为Option可以被看作是包含零个或者一个元素的容器,flatten对于Some和None元素的序列很有用。它会将Some中的值取出来新建一个列表,然后丢掉None元素(flatten--> kill None):
scala> val x = Vector(Some(1), None, Some(3), None)
x: scala.collection.immutable.Vector[Option[Int]] = Vector(Some(1), None, Some(3), None)
scala> x.flatten
res2: scala.collection.immutable.Vector[Int] = Vector(1, 3)
(b)
问题:对于flatMap方法,如何使用它?哪里使用它?
当在map后需要使用flatten时使用flatMap。具体情况是:
- 在使用map方法(或者for/yield表达式)根据一个已有的集合创建一个新的集合。
- 结果集合是一个列表的列表。
- 可以在map(或者一个for/yield表达式)后立刻调用flatten。
当遇到这些情况时,可以使用flatMap。...通用法则:无论何时想要在map后调用flatten,就用flatMap。
(二)
spark中的map和flatMap区别如上述描述类似。附图解释:
(三)
曾经遇到一个bug:
value sortByKey is not a member of org.apache.spark.rdd.RDD[scala.collection.immutable.Map[Double,(scala.collection.immutable.Set[Int], Int)]]
原因是因为 sortByKey应该适用于如下类型的:
scala> val oneFIS = rawTrans.flatMap(line=>line).map((_,1))
oneFIS: org.apache.spark.rdd.RDD[(Int, Int)] = MapPartitionsRDD[4] at map at <console>:28
即sortByKey()适用于RDD[(k, v)]这种类型,而不是RDD[Map[k, v]]这种类型;不然会提示value sortByKey is not a member of org.apache.spark.rdd.RDD[Map[k, v]]之类的错误。
所以,只要将代码中的map修改为flatMap即可!这算是使用map和使用flatMap的一个场景区别罢!
主要参考:《Spark快速大数据分析》、《Scalabianch》