Scala是函數式編程,這點在集合操作中大量體現。高階函數,也就是能夠接收另外一個函數作為參數的函數。
假如現在有一個需要是將List集合中的每個元素變為原來的兩倍,現在來對比Java方式實現和Scala方式實現區別
Java方式實現,先將集合中的每個元素遍歷出來,然后再乘以2,塞到另外一個集合中
ArrayList<Integer> list1 = new ArrayList<Integer>(); list1.add(3); list1.add(5); list1.add(7); ArrayList<Integer> list2 = new ArrayList<Integer>(); for (Integer elem : list1) { list2.add(elem * 2); } System.out.println(list2);
Scala方式實現
val list1 = List(3, 5, 7) val list2 = list1.map(multiple) //map高階函數,能夠接收另外一個函數 def multiple(n1: Int): Int = {
2 * n1
}
println(list2)
可以發現相對於Java的實現方式,Scala中更偏向於使用高階函數來解決問題,並且也簡化了很多。
或許你會有些許疑問,這是什么鬼,這沒有簡化到哪里呀!的確,但是這里只是小小的演示。
Scala中常用的高階函數有如下幾種
1.映射函數(map)
map函數
小注:在Scala中的泛型是表示方法是“[]”,java中的泛型表示方式是“<>”。map函數存在於所有集合類型中,包括在String中。
現在再看前面的實例,它是這樣來執行的
- 首先依次遍歷list1集合的元素
- 將各個元素傳遞給Multiple函數,計算並返回
- 將返回結果放到一個新的集合中,並賦給list2
- 輸出結果
為了能夠更好的理解,嘗試編寫一個List,來模擬List
object Demo_021{ def main(args: Array[String]): Unit = { val myList = MyList() val myList2 = myList.map(multiple) //調用map高階函數,並傳入mutiple函數 println("myList2=" + myList2) println("myList=" + myList.list1) } def multiple(n1: Int): Int = { 2 * n1 } } //伴生類,模擬List class MyList { var list1 = List(3, 5, 7) var list2 = List[Int]() //map高階函數,接收另外一個函數作為參數, // f:Int=>Int : f表示是函數,:Int表示所傳入的函數f的參數類型,必須是Int型,=>Int表示所傳入函數f的返回值為Int // : List[Int] :表示Map函數的返回值為List[Int] def map(f:Int=>Int): List[Int] = { for (item<-list1) { list2 = list2 :+ f(item) //f(item) 表示調用所傳入的函數,每次執行都會將返回寫過寫入到list2中 } list2 //返回list2,未明確指定返回值,以函數最后一行的執行結果作為返回值 } } //伴生對象 object MyList { //使用apply方式實例化 def apply(): MyList = new MyList() }
運行結果:
模擬有些拙劣,但是基本能夠說明問題,map方法在List底層所實現時,也是逐個遍歷並執行所傳入的函數,最后返回執行結果集合
下面是List中map函數的源碼,實際List集合底層在調用map方法的時候所做的操作和上面類似
使用實例1:將 val names = List("Alice", "Bob", "Nick") 中的所有單詞,全部轉成字母大寫,返回到新的List集合中.
object Demo_022 { def main(args: Array[String]): Unit = { val names = List("Alice", "Bob", "Nick") val names2 = names.map(upper) println("names=" + names2) } def upper(s:String): String = { s.toUpperCase } }
執行結果:
2.扁平化(flatMap)
flatmap:所謂扁平化,就是將集合中的每個元素的子元素映射到某個函數並返回新的集合。
實例:
object Demo_022 { def main(args: Array[String]): Unit = { val names = List("Alice", "Bob", "Nick") //相當於在原來map高階函數的基礎上做了二次循環,將元素進一步打散 val names2 = names.flatMap(upper) println("names=" + names2) } def upper(s:String): String = { s.toUpperCase } }
運行結果:
3.過濾(filter)
filter:將符合要求的數據(篩選)放置到新的集合中
應用案例:將 val names = List("Alice", "Bob", "Nick") 集合中首字母為'A'的篩選到新的集合。
object Demo_025 { def main(args: Array[String]): Unit = { val names = List("Alice", "Bob", "Nick") def startA(s: String): Boolean = { s.startsWith("A") } val names2 = names.filter(startA) //表示調用filter高階函數 println("names=" + names2) } }
運行結果:
還有更為簡潔的操作:
// val names2: List[String] = names.filter((x:String)=>x.startsWith("A")) val names2: List[String] = names.filter(_.startsWith("A"))
filter函數在執行過程中,類似於map函數,將符合條件的篩選出來放到一個集合中。
4.化簡(reduce、reduceLeft、reduceRight)
化簡:將二元函數引用於集合中的函數。有三種類型的函數,reduce、reduceLeft和reduceRight,其中reduce等同於reduceLeft。
reduceLeft(f) 接收的函數需要的形式為 op: (B, A) => B): B,
reduceleft(f) 的運行規則是 從左邊開始執行將得到的結果返回給第一個參數,然后繼續和下一個元素運行,將得到的結果繼續返回給第一個參數,繼續.。
reduceRight的運行規則和reduceRight類似,只是從右往左執行
實例1:val list = List(1,2,3,4,5) , 求出list的和
object Demo_026 { def main(args: Array[String]): Unit = { val list = List(1, 2, 3, 4, 5) def sum(n1: Int, n2: Int): Int = { n1 + n2 } val res1 = list.reduceLeft(sum) println("res=" + res1) } }
輸出為60。
實例2:觀察reduce、reduceRight和reduceLeft在求List(1, 2, 3, 4 ,5)中元素差值時的表現
object Demo_027 { def main(args: Array[String]): Unit = { val list = List(1, 2, 3, 4 ,5) def minus( num1 : Int, num2 : Int ): Int = { num1 - num2 } println(list.reduceLeft(minus)) // 輸出-13 println(list.reduceRight(minus)) //輸出3 println(list.reduce(minus)) //輸出-13 } }
運行結果為
綜述:reduce等同於reduceLeft、執行規則從左向右,而reduceRight執行規則是從右向左。
另外,還可以使用化簡來求出一個集合的最值
object Demo_027 { def main(args: Array[String]): Unit = { val list = List(1, 2, 3, 4 ,5) def max( num1 : Int, num2 : Int ): Int = { if(num1<num2){ num2 }else{ num1 } } println(list.reduceLeft(max)) // 求list中的最大值 } }
簡化形式是:
val result: Int = list.reduceLeft((num1,num2)=>{if(num1<num2) num2 else num1})
5.折疊(foldLeft、foldRight、fold)
fold函數將上一步返回的值作為函數的第一個參數繼續傳遞參與運算,直到list中的所有元素被遍歷。有三種函數形式:fold、foldLeft和folderRight。
fold函數在使用上基本和reduce函數在使用上基本相同,甚至reduceLeft函數的底層,就是調用foldLeft函數
觀察如下實例
object Demo_028 { def main(args: Array[String]): Unit = { val list = List(1, 2, 3, 4) def minus(n1: Int, n2: Int): Int = { n1 - n2 } println(list.foldLeft(5)(minus))
println(list.foldRight(5)(minus))
} }
輸出結果為:
它的執行過程是這樣的:
另外foldLeft和foldRight 縮寫方式分別是:/:和:\
object Demo_028 { def main(args: Array[String]): Unit = { val list = List(1, 2, 3, 4) def minus(n1: Int, n2: Int): Int = { n1 - n2 } println((5 /: list)(minus)) //等價於list.foldLeft(5)(minus) println((list :\ 5)(minus)) //list.foldRight(5)(minus) } }
可以使用folderLeft統計字母出現的次數,還可以用來統計文本中單詞出現的次數
6.掃描(scanLeft、scanRight)
掃描,即對某個集合的所有元素做fold操作,但是會把產生的所有中間結果放置於一個集合中保存
object Demo_029 { def main(args: Array[String]): Unit = { def minus( num1 : Int, num2 : Int ) : Int = { num1 - num2 } //5 (1,2,3,4,5) =>(5,4,2,-1,-5,-10) val i8 = (1 to 5).scanLeft(5)(minus) //IndexedSeq[Int] println(i8) def add( num1 : Int, num2 : Int ) : Int = { num1 + num2 } //5 (1,2,3,4,5) =>(5,6,8, 11,15,20) val i9 = (1 to 5).scanLeft(5)(add) //IndexedSeq[Int] println(i9) } }
輸出結果為:
觀察另外一個實例
object Demo_030 { def main(args: Array[String]): Unit = { def test(num1:Int,num2:Int): Int ={ num1 * num2 } var result=(1 to 3).scanLeft(3)(test) println(result) } }
運行結果
綜述,scanLeft執行類似於folderLeft,只是它會將中間結果緩存下來。