函數(shù)式編程
我們將來使用Spark/Flink的大量業(yè)務(wù)代碼都會使用到函數(shù)式編程擎场。下面的這些操作是學(xué)習(xí)的重點(diǎn)。
遍歷( foreach )
映射( map )
映射扁平化( flatmap )
過濾( filter )
是否存在( exists )
排序( sorted 、 sortBy 、 sortWith )
分組( groupBy )
聚合計(jì)算( reduce )
折疊( fold )
遍歷 | foreach
之前捻激,學(xué)習(xí)過了使用for表達(dá)式來遍歷集合钧排。我們接下來將學(xué)習(xí)scala的函數(shù)式編程敦腔,使
用 foreach 方法來進(jìn)行遍歷、迭代恨溜。它可以讓代碼更加簡潔符衔。
語法
foreach(f: (A) ? Unit): Unit
解析
foreach | API | 說明 |
---|---|---|
參數(shù) | f: (A) ? Unit | 接收一個(gè)函數(shù)對象,函數(shù)對象的輸入?yún)?shù)為集合的元素糟袁,返回值為空 |
返回值 | Unit | 空 |
foreach執(zhí)行過程
書寫參數(shù)類型的遍歷
示例
有一個(gè)列表判族,包含以下元素1,2,3,4,請使用foreach方法遍歷打印每個(gè)元素
// 定義一個(gè)列表
scala> val a = List(1,2,3,4)
a: List[Int] = List(1, 2, 3, 4)
// 迭代打印
scala> a.foreach((x:Int)=>println(x))
使用類型推斷簡化函數(shù)定義
上述案例函數(shù)定義有點(diǎn)啰嗦项戴,我們有更簡潔的寫法形帮。因?yàn)槭褂胒oreach去迭代列表,而列表中的每
個(gè)元素類型是確定的
scala可以自動來推斷出來集合中每個(gè)元素參數(shù)的類型
創(chuàng)建函數(shù)時(shí),可以省略其參數(shù)列表的類型
示例:
有一個(gè)列表辩撑,包含以下元素1,2,3,4界斜,請使用foreach方法遍歷打印每個(gè)元素
使用類型推斷簡化函數(shù)定義
scala> val a = List(1,2,3,4)
a: List[Int] = List(1, 2, 3, 4)
// 省略參數(shù)類型
scala> a.foreach(x=>println(x))
使用下劃線來簡化函數(shù)定義
當(dāng)函數(shù)參數(shù),只在函數(shù)體中出現(xiàn)一次合冀,而且函數(shù)體沒有嵌套調(diào)用時(shí)各薇,可以使用下劃線來簡化函數(shù)定
義
示例:
有一個(gè)列表,包含以下元素1,2,3,4君躺,請使用foreach方法遍歷打印每個(gè)元素
使用下劃線簡化函數(shù)定義
scala> val a = List(1,2,3,4)
a: List[Int] = List(1, 2, 3, 4)
a.foreach(println(_))
如果方法參數(shù)是函數(shù)峭判,如果出現(xiàn)了下劃線,scala編譯器會自動將代碼封裝到一個(gè)函數(shù)中
參數(shù)列表也是由scala編譯器自動處理
映射 | map
集合的映射操作是將來在編寫Spark/Flink用得最多的操作棕叫,是我們必須要掌握林螃。
語法
def map[B](f: (A) ? B): TraversableOnce[B]
解析
map方法 | API | 說明 |
---|---|---|
泛型 | [B] | 指定map方法最終返回的集合泛型 |
參數(shù) | f: (A) ? B | 傳入一個(gè)函數(shù)對象,該函數(shù)接收一個(gè)類型A(要轉(zhuǎn)換的列表元素)俺泣,返回值為類型B |
返回值 | TraversableOnce[B] | B類型集合 |
示例:
創(chuàng)建一個(gè)列表疗认,包含元素1,2,3,4
對List中的每一個(gè)元素加1
scala> a.map(x=>x+1)
res4: List[Int] = List(2, 3, 4, 5)
示例:
創(chuàng)建一個(gè)列表,包含元素1,2,3,4
使用下劃線來定義函數(shù)砌滞,對List中的每一個(gè)元素加1
scala> val a = List(1,2,3,4)
a: List[Int] = List(1, 2, 3, 4)
scala> a.map(_ + 1)
扁平化映射 | flatmap
映射扁平化也是將來用得非常多的操作侮邀,也是必須要掌握的。
- map是將列表中的元素轉(zhuǎn)換為一個(gè)List
- flatten再將整個(gè)列表進(jìn)行扁平化
語法
def flatMap[B](f: (A) ? GenTraversableOnce[B]): TraversableOnce[B]
方法解析
flatmap方法 | API | 說明 |
---|---|---|
泛型 | [B] | 最終要轉(zhuǎn)換的集合元素類型 |
參數(shù) | f: (A) ? GenTraversableOnce[B] | 傳入一個(gè)函數(shù)對象贝润,函數(shù)的參數(shù)是集合的元素绊茧,函數(shù)的返回值是一個(gè)集合 |
返回值 | TraversableOnce[B] | B類型的集合 |
示例
- 有一個(gè)包含了若干個(gè)文本行的列表:"hadoop hive spark flink flume", "kudu hbase sqoop
storm"
- 獲取到文本行中的每一個(gè)單詞,并將每一個(gè)單詞都放到列表中
步驟
使用map將文本行拆分成數(shù)組
再對數(shù)組進(jìn)行扁平化
如果不使用flatmap來處理代碼
// 定義文本行列表
scala> val a = List("hadoop hive spark flink flume", "kudu hbase sqoop storm")
a: List[String] = List(hadoop hive spark flink flume, kudu hbase sqoop storm)
// 使用map將文本行轉(zhuǎn)換為單詞數(shù)組
scala> a.map(x=>x.split(" "))
res5: List[Array[String]] = List(Array(hadoop, hive, spark, flink, flume), Array(kudu, hbase, sqoop, storm))
// 扁平化打掘,將數(shù)組中的元素扁平
scala> a.map(x=>x.split(" ")).flatten
res6: List[String] = List(hadoop, hive, spark, flink, flume, kudu, hbase, sqoop, storm)
使用flatmap簡化操作
scala> val a = List("hadoop hive spark flink flume", "kudu hbase sqoop storm")
a: List[String] = List(hadoop hive spark flink flume, kudu hbase sqoop storm)
scala> a.flatMap(_.split(" "))
res7: List[String] = List(hadoop, hive, spark, flink, flume, kudu, hbase, sqoop, storm)
過濾 | fliter
過濾符合一定條件的元素
語法
def filter(p: (A) ? Boolean): TraversableOnce[A]
方法解析
fliter方法 | API | 說明 |
---|---|---|
參數(shù) | p: (A) ? Boolean | 傳入一個(gè)函數(shù)對象华畏,接收一個(gè)集合類型的參數(shù) ,返回布爾類型尊蚁,滿足條件返回true, 不滿足返回false |
返回值 | TraversableOnce[A] | 列表 |
示例
有一個(gè)數(shù)字列表亡笑,元素為:1,2,3,4,5,6,7,8,9
請過濾出所有的偶數(shù)
scala> List(1,2,3,4,5,6,7,8,9).filter(_ % 2 == 0)
res8: List[Int] = List(2, 4, 6, 8)
默認(rèn)排序 | sorted
示例
定義一個(gè)列表,包含以下元素: 3, 1, 2, 9, 7
對列表進(jìn)行升序排序
scala> List(3,1,2,9,7).sorted
res16: List[Int] = List(1, 2, 3, 7, 9)
指定字段排序 | sortBy
根據(jù)傳入的函數(shù)轉(zhuǎn)換后横朋,再進(jìn)行排序
示例
有一個(gè)列表仑乌,分別包含幾下文本行:"01 hadoop", "02 flume", "03 hive", "04 spark"
請按照單詞字母進(jìn)行排序
scala> val a = List("01 hadoop", "02 flume", "03 hive", "04 spark")
a: List[String] = List(01 hadoop, 02 flume, 03 hive, 04 spark)
// 獲取單詞字段
scala> a.sortBy(_.split(" ")(1))
res8: List[String] = List(02 flume, 01 hadoop, 03 hive, 04 spark)
自定義排序 | sortWith
示例
有一個(gè)列表,包含以下元素:2,3,1,6,4,5
使用sortBy對列表進(jìn)行降序排序
如果不使用sortwith步驟
scala> val a = List(2,3,1,6,4,5)
a: List[Int] = List(2, 3, 1, 6, 4, 5)
scala> a.sortWith((x,y) => if(x<y)true else false)
res15: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> res15.reverse
res18: List[Int] = List(6, 5, 4, 3, 2, 1)
使用sortwith
scala> val a = List(2,3,1,6,4,5)
a: List[Int] = List(2, 3, 1, 6, 4, 5)
// 函數(shù)參數(shù)只在函數(shù)中出現(xiàn)一次琴锭,可以使用下劃線代替
scala> a.sortWith(_ < _).reverse
res19: List[Int] = List(6, 5, 4, 3, 2, 1)
分組 | groupBy
我們?nèi)绻獙?shù)據(jù)按照分組來進(jìn)行統(tǒng)計(jì)分析晰甚,就需要使用到分組方法
語法
def groupBy[K](f: (A) ? K): Map[K, List[A]]
示例
- 有一個(gè)列表,包含了學(xué)生的姓名和性別:
"張三", "男"
"李四", "女"
"王五", "男"
- 請按照性別進(jìn)行分組决帖,統(tǒng)計(jì)不同性別的學(xué)生人數(shù)
步驟
定義一個(gè)元組列表來保存學(xué)生姓名和性別
按照性別進(jìn)行分組
將分組后的Map轉(zhuǎn)換為列表:List(("男" -> 2), ("女" -> 1))
scala> val a = List("張三"->"男", "李四"->"女", "王五"->"男")
a: List[(String, String)] = List((張三,男), (李四,女), (王五,男))
// 按照性別分組
scala> a.groupBy(_._2)
res0: scala.collection.immutable.Map[String,List[(String, String)]] = Map(男 -> List(( 張三,男), (王五,男)), 女 -> List((李四,女)))
// 將分組后的映射轉(zhuǎn)換為性別/人數(shù)元組列表
scala> res0.map(x => x._1 -> x._2.size)
res3: scala.collection.immutable.Map[String,Int] = Map(男 -> 2, 女 -> 1)
聚合 | reduce
reduce表示將列表厕九,傳入一個(gè)函數(shù)進(jìn)行聚合計(jì)算
語法:
def reduce[A1 >: A](op: (A1, A1) ? A1): A1
示例
定義一個(gè)列表,包含以下元素:1,2,3,4,5,6,7,8,9,10
使用reduce計(jì)算所有元素的和
scala> val a = List(1,2,3,4,5,6,7,8,9,10)
a: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> a.reduce((x,y) => x + y)
res5: Int = 55
// 第一個(gè)下劃線表示第一個(gè)參數(shù)地回,就是歷史的聚合數(shù)據(jù)結(jié)果
// 第二個(gè)下劃線表示第二個(gè)參數(shù)扁远,就是當(dāng)前要聚合的數(shù)據(jù)元素
scala> a.reduce(_ + _)
res53: Int = 55
// 與reduce一樣俊鱼,從左往右計(jì)算
scala> a.reduceLeft(_ + _)
res0: Int = 55
// 從右往左聚合計(jì)算
scala> a.reduceRight(_ + _)
res1: Int = 55
折疊 | fold
fold與reduce很像,但是多了一個(gè)指定初始值參數(shù)
語法
def fold[A1 >: A](z: A1)(op: (A1, A1) ? A1): A1
示例
定義一個(gè)列表畅买,包含以下元素:1,2,3,4,5,6,7,8,9,10
使用fold方法計(jì)算所有元素的和
scala> val a = List(1,2,3,4,5,6,7,8,9,10)
a: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> a.fold(0)(_ + _)
res4: Int = 155
fold和foldLet效果一致并闲,表示從左往右計(jì)算
foldRight表示從右往左計(jì)算
z的值為初始值,比如寫10皮获,就是從10開始累加焙蚓,如果是上述的0,則從0開始累加