Spark 當中map,flatMap,mapPartitions的區別以及示例

這幾天學習看了map以及flatMap還有mapPartitions,然後寫一篇博文記錄一哈,以免自己忘了,如果有寫錯的地方,一定要不留情面的指出來!

Spark 當中map,flatMap,mapPartitions
map:對集合中每個元素進行操作。
flatMap:對集合中每個元素進行操作然後再扁平化(打平)。
mapPartitions:與map類似,函數會對每個分區中的一組數據進行相應的操作(把每個分區的數據當成map當中每個元素,可以着了類比)。

接下來就詳細看一下每個方法傳什麼參數,然後返回的結果有什麼不同。

1.map

接口定義如下:

def map[U](f : scala.Function1[T, U])(implicit evidence$3 : scala.reflect.ClassTag[U]) : org.apache.spark.rdd.RDD[U]

一句話概括:
對集合中每個元素進行操作,你map裏面傳入的方法返回的數據是U類型,map就返回個包裝好的RDD[U]類型

map接口的意思是入參是傳入一個方法(方法的入參是對象T,出參是對象U),然後map接口會返回一個你傳入方法的返回對象U的RDD封裝。即爲RDD[U]。
可能這麼說有點抽象,看一哈代碼可能就很清晰了嗷

object mapTest {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("mapTest").setMaster("local")
    val sc = new SparkContext(conf)
    val data = Array("name:xiaohuangzi","age:30","profession:student")
    val lines = sc.parallelize(data,1)
    val resultRdd = lines.map(x=>x.split(":"))
    resultRdd.foreach(x=>println(x(0)+"   :    "+x(1)))
  }
}

lines的類型就是RDD[String],lines.map中,其中輸入參數就是String類型,輸出參數就是x.split(": ")即String[]類型,所以resultRdd 的類型就是RDD[String[]]。所以在遍歷resultRdd 的時候,相當於遍歷每個String[],所以我們需要用X(0),X(1)的方式來獲取數據,來看一下結果。
在這裏插入圖片描述
如果你是在不知道返回的是啥類型,記住一句話就行,就是你傳入map裏的方法返回的是啥,那麼map就返回什麼類型的RDD,上面的例子很好的體現了哦。

2.flatMap

def flatMap[U](f : scala.Function1[T, scala.TraversableOnce[U]])(implicit evidence$4 : scala.reflect.ClassTag[U]) : org.apache.spark.rdd.RDD[U]

其中flatMap傳入的方法當中,輸入參數是對象T,輸出參數是一個TraversableOnce[U],可以理解爲對象U的集合,然後最後flatMap返回一個RDD[u]。

TraversableOnce 圖如下:
在這裏插入圖片描述一句話概括:
對集合中每個元素進行操作返回一個集合,最後在扁平化返回一個對象。你flatMap裏面傳入的方法返回的數據是 集合 U類型,flatMap就將集合U類型扁平化,返回一個RDD[U]類型。比如你傳入的方法返回的是List< String >/String[],那麼flatMap將返回RDD[String]

然後再來看個跟map差不多的例子

object flatMapTest {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("mapTest").setMaster("local")
    val sc = new SparkContext(conf)
    val data = Array("name:xiaoming","age:30","profession:student")
    val lines = sc.parallelize(data,1)
    val resultRdd = lines.flatMap(x=>x.split(":"))
    resultRdd.foreach(x=>println(x))
  }
}

lines的類型就是RDD[String],lines.flatMap中,其中輸入參數就是String類型,輸出參數就是x.split(": ")即String[]類型,算一個集合;所以resultRdd 的類型就是RDD[String](將這個集合打平,)。所以在遍歷resultRdd 的時候,相當於遍歷每個String,所以我們直接用X的方式來獲取數據,來看一下結果

在這裏插入圖片描述這個結果很顯然,flatMap傳入方法的返回參數是String[],所以flatMap返回的RDD[String],將String[]打平,成爲一個統一的RDD[String]類型。

再來看一個非常有趣的例子:

object flatMapTest {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("mapTest").setMaster("local")
    val sc = new SparkContext(conf)
    val arr2=sc.parallelize(Array(("a",1),("b",2),("c",3)))
    val result2 = arr2.flatMap(x=>(x._1+x._2))
    result2.foreach(x=>println(x))
  }
}

一看到這個例子,是不是猛然覺得,這個代碼能運行起來嘛,因爲flatMap傳入的方法返回參數不是要一個集合嘛,但是String算集合嘛?
在這裏插入圖片描述算的,所以運行出來的結果是
在這裏插入圖片描述
再來看一個例子,加深一下印象

object flatMapTest {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("mapTest").setMaster("local")
    val sc = new SparkContext(conf)
    val data = Array("name:xiaohuangzi","age:30","profession:student")
    val lines = sc.parallelize(data,1)
    val resultRdd = lines.flatMap(x=>x.split(":")).flatMap(x=>x)
    resultRdd.foreach(x=>print(" * "+x))
  }
}

在這裏插入圖片描述結果如上。其實原理跟上上個例子一樣,異曲同工之妙吧。

3.mapPartitions

mapPartitions接口定義如下:

def mapPartitions[U](f : scala.Function1[scala.Iterator[T], scala.Iterator[U]], preservesPartitioning : scala.Boolean = { /* compiled code */ })(implicit evidence$6 : scala.reflect.ClassTag[U]) : org.apache.spark.rdd.RDD[U] = { /* compiled code */ }

一句話概括:mapPartitions中傳入的方法輸入參數是T對象的迭代器,返回對象是U對象的迭代器,最終mapPartitions返回是RDD[U],map是針對每個元素進行操作,mapPartitions是針對不同分區的數據進行操作。假設有10個元素,3個分區,你需要在map或者mapPartitions裏建立數據庫鏈接,那麼map會建立10次,而mapPartitions會只建立3次鏈接,這就是mapPartitions的優點

接下來看個例子,就能看出有啥不同了

object mapPartitionTest {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("mapPartitionTest").setMaster("local")
    val sc = new SparkContext(conf)
    val a = sc.parallelize(1 to 10, 3)
    val result1 = a.map(x=>{println("map : " + x);x*2})
    val result2 = a.mapPartitions(iterator=>{println("mapPartition start");val res = for(x <- iterator) yield x*2;res} )
    println("result1 iterator :")
    result1.foreach(x=>println(x))
    println("result2 iterator :")
    result2.foreach(x=>println(x))
  }
}

在這裏插入圖片描述
在這裏插入圖片描述
所以我們可以看出,map是針對每個元素,也就是10個元素進行的;而mapPartitions則是針對我們設置的3個分區,遍歷了3次。

而且我們可以發現,其實result1和result2的數據類型都是RDD[Int],並且數據都是擴大了2倍,是一樣的,所以有的時候根據具體情況,map和mapPartitions是可以互換的,達到效果一樣的目的,但是mapPartitions的效果比map好,要根據實際情況替換。

好了,都講解完了,如果有錯就給我留言,我好及時改正,及時進步。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章