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,隻是它會将中間結果緩存下來。