11.1 集合元素的映射-map映射操作 11.1.1 看一個實際需求 要求:請將List(3,5,8)中所有的元素都*2,將其結果放到一個新的集合中返回,即返回一個新的List(6,10,16),請編寫程式實現 11.1.2 map映射操作 11.1.3 使用傳統方法 -案例演示 -上述案例演示的 ...
11.1 集合元素的映射-map映射操作
11.1.1 看一個實際需求
要求:請將List(3,5,8)中所有的元素都*2,將其結果放到一個新的集合中返回,即返回一個新的List(6,10,16),請編寫程式實現
11.1.2 map映射操作
11.1.3 使用傳統方法
-案例演示
object boke_demo01 { def main(args: Array[String]): Unit = { /* 請將List(3,5,8) 中的所有元素都 * 2 , 將其結果放到一個新的集合中返回,即返回一個新的List(6,10,16), 請編寫程式實現. */ val list1 = List(3, 5, 8) //集合 var list2 = List[Int]() //新的集合,準備放入新的內容 for (item <- list1) { //遍歷 list2 = list2 :+ item * 2 // 對元素*2 ,然後加入list2集合 } println("list2=" + list2) //List(6,10,16) } }
-上述案例演示的分析和小結
1) 優點
處理方法比較直接,好理解
2) 缺點
不夠簡潔高效
沒有體現函數式編程特點 集合 => 函數 => 新的集合 => 函數 ...
不利於處理複雜的數據處理業務
11.1.4 高階函數基本使用案例1
-案例演示
object boke_demo01 { def main(args: Array[String]): Unit = { //使用高階函數 val res = test(sum2 _, 3.5) println("res=" + res) //在scala中,可以把一個函數直接賦給一個變數,但是不執行函數 val f1 = myPrint _ f1() //執行 } def myPrint(): Unit = { println("hello,world!") } //說明 //1. test就是一個高階函數 //2. f: Double => Double 表示一個函數, 該函數可以接受一個Double,返回Double //3. n1: Double 普通參數 //4. f(n1) 在test函數中,執行 你傳入的函數 def test(f: Double => Double, n1: Double) = { f(n1) } //普通的函數, 可以接受一個Double,返回Double def sum2(d: Double): Double = { println("sum2被調用") d + d } }
11.1.5 高階函數應用案例2
-案例演示
object boke_demo01 { def main(args: Array[String]): Unit = { test2(sayOK) } //說明test2是一個高階函數,可以接受一個 沒有輸入,返回為Unit的函數 def test2(f: () => Unit) = { f() } def sayOK() = { println("sayOKKK...") } def sub(n1: Int): Unit = { } }
11.1.6 使用map映射函數來解決
//請將 List(3,5,8) 中的所有元素都 * 2 , 將其結果放到一個新的集合中返回,即返回一個新的 List(6,10,16), 請編寫程式實現. val list = List(3, 5, 8) //說明 list.map(multiple) 做了什麼 //1. 將 list 這個集合的元素 依次遍歷 //2. 將各個元素傳遞給 multiple 函數 => 新 Int //3. 將得到新 Int ,放入到一個新的集合併返回 //4. 因此 multiple 函數調用 3 val list2 = list.map(multiple) println("list2=" + list2) //List(6,10,16) def multiple(n: Int): Int = { println("multiple 被調用~~") 2 * n }
11.1.7 深刻理解map映射函數的機制-模擬實現
//深刻理解 map 映射函數的機制-模擬實現 class MyList { val list1 = List(3, 5, 8, 9) //新的集合 var list2 = List[Int]() //寫 map def map(f: Int => Int): List[Int] = { //遍歷集合 for (item <- this.list1) { //過濾,扁平化。。。 list2 = list2 :+ f(item) } list2 } } object MyList { def apply(): MyList = new MyList() }
11.1.9 flatmap映射: flat即壓扁,壓平,扁平化映射
-扁平化說明
flatmap:flat即壓扁,壓平,扁平化,效果就是將集合中的每個元素的子元素映射到某個函數並返回新的集合
-案例演示
object boke_demo01 { def main(args: Array[String]): Unit = { val names = List("Alice", "Tom", "Nick") //需求是將List集合中的所有元素,進行扁平化操作,即把所有元素打散 val names2 = names.flatMap(upper) println("names2=" + names2) } def upper(s: String): String = { s.toUpperCase } }
11.2 集合元素的過濾-filter
-基本說明
filter:將符合要求的數據(篩選)放置到新的集合中
-案例演示
應用案例:將 val names = List("Alice","Tom","Nick") 集合中首字母為'A'的篩選到新的集合
思考:如果這個使用傳統的方法,如何完成?
object boke_demo01 { def main(args: Array[String]): Unit = { /* 選出首字母為A的元素 */ val names = List("Alice", "Tom", "Nick") val names2 = names.filter(startA) println("names=" + names) println("names2=" + names2) } def startA(str: String): Boolean = { str.startsWith("A") } }
11.3 化簡
11.3.1 看一個需求
val list = List(1,20,30,4,5),求出list的和
11.3.2 化簡的介紹:
化簡:將二元函數引用於集合中的函數
上面的問題當然可以使用遍歷list方法來解決,這裡使用scala的簡化方式來完成
11.3.3 案例演示
object boke_demo01 { def main(args: Array[String]): Unit = { /* 使用化簡的方式來計算list集合的和 */ val list = List(1, 20, 30, 4, 5) val res = list.reduceLeft(sum) //接收一個函數時,也可以直接傳入一個匿名函數 //執行的流程分析 //步驟 1 (1 + 20) //步驟 2 (1 + 20) + 30 //步驟 3 ((1 + 20) + 30) + 4 //步驟 4 (((1 + 20) + 30) + 4) + 5 = 60 println("res=" + res) // 60 } def sum(n1: Int, n2: Int): Int = { println("sum被調用~~") n1 + n2 } }
11.3.4 對reduceLeft的運行機制的說明
1) def reduceLeft[B >: A](@deprecatedName('f) op: (B,A) => B): B
2) reduceLeft(f) 接收的函數需要的形式為 op: (B,A) => B): B
3) reduceLeft(f) 的運行規則是 從左邊開始執行將得到的結果返回給第一個參數
4) 然後繼續和下一個元素運行,將得到的結果繼續返回給第一個參數,繼續...
即: //((((1+2)+3)+4)+5) = 15
11.4 摺疊
11.4.1 基本介紹
-fold函數將上一步返回的值作為函數的第一參數繼續傳遞參與運算,直到list中的所有元素被遍歷
-可以把reduceLeft看做簡化版的foldLeft
如何理解:
def reduceLeft[B >: A](@deprecatedName('f) op: (B, A) => B): B = if (isEmpty) throw new UnsupportedOperationException("empty.reduceLeft") else tail.foldLeft[B](head)(op) //可以看到. reduceLeft 就是調用的 foldLeft[B](head),並且是預設從集合的 head 元素開始操作的。
-相關函數:fold,foldLeft,foldRight,可以參考reduce的相關方法理解
11.4.2 應用案例
看下麵代碼看看輸出什麼,並分析原因
案例演示
object boke_demo01 { def main(args: Array[String]): Unit = { val list = List(1, 2, 3, 4) def minus(num1: Int, num2: Int): Int = { num1 - num2 } //說明 //1. 摺疊的理解和化簡的運行機制幾乎一樣. //理解 list.foldLeft(5)(minus) 理解成 list(5,1, 2, 3, 4) list.reduceLeft(minus) //步驟 (5-1) //步驟 ((5-1) - 2) //步驟 (((5-1) - 2) - 3) //步驟 ((((5-1) - 2) - 3)) - 4 = - 5 println(list.foldLeft(5)(minus)) // 函數的柯里化 ////理解 list.foldRight(5)(minus) 理解成 list(1, 2, 3, 4, 5) list.reduceRight(minus) // 步驟 (4 - 5) // 步驟 (3- (4 - 5)) // 步驟 (2 -(3- (4 - 5))) // 步驟 1- (2 -(3- (4 - 5))) = 3 println(list.foldRight(5)(minus)) // } }
11.4.3 foldLeft和foldRight縮寫方法分別是:/:和:\
-案例演示
object boke_demo01 { def main(args: Array[String]): Unit = { val list4 = List(1, 9) def minus(num1: Int, num2: Int): Int = { num1 - num2 } var i6 = (1 /: list4) (minus) // =等價=> list4.foldLeft(1)(minus) println("i6=" + i6) i6 = (100 /: list4) (minus) //=等價=> list4.foldLeft(100)(minus) println(i6) // 輸出? i6 = (list4 :\ 10) (minus) // list4.foldRight(10)(minus) println(i6) // 輸出? 2 } }
11.5 掃描
11.5.1 基本介紹
掃描,即對某個集合的所有元素做fold操作,但是會把產生的所有中間結果放置於一個集合中保存
11.5.2 應用案例
object boke_demo01 { 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) //Vector(5, 4, 2, -1, -5, -10) val i8 = (1 to 5).scanLeft(5)(minus) //IndexedSeq[Int] println("i8=" + i8) //普通函數 def add( num1 : Int, num2 : Int ) : Int = { num1 + num2 } //(1,2,3,4,5) 5 => (20,19,17,14, 10,5) val i9 = (1 to 5).scanRight(5)(add) //IndexedSeq[Int] println("i9=" + i9) } }
11.7 擴展-拉鏈(合併)
11.7.1 基本介紹
在開發中,當我們需要將兩個集合進行 對偶元組合併,可以使用拉鏈
11.7.2 應用案例
object boke_demo01 { def main(args: Array[String]): Unit = { // 拉鏈 val list1 = List(1, 2, 3) val list2 = List(4, 5, 6) val list3 = list1.zip(list2) // (1,4),(2,5),(3,6) println("list3=" + list3) } }
11.7.3 拉鏈的使用註意事項
1) 拉鏈的本質就是兩個集合的合併操作,合併後每個元素是一個 對偶元組
2) 操作的規則如下圖
3) 如果兩個集合個數不對應,會造成數據丟失
4) 集合不限於List,也可以是其它集合,比如:Array
5) 如果要取出合併後的各個對偶元組的數據,可以遍歷
for(item<-list3) { print(item._1 + " " + item._2) //取出時,按照元組的方式取出即可 }
11.8 擴展-迭代器
11.8.1 基本說明
通過iterator方法從集合獲得一個迭代器,通過while迴圈和for表達式對集合進行遍歷(學習使用迭代器來遍歷)
11.8.2 應用案例
object boke_demo01 { def main(args: Array[String]): Unit = { val iterator = List(1, 2, 3, 4, 5).iterator // 得到迭代器 /* 這裡我們看看iterator 的繼承關係 def iterator: Iterator[A] = new AbstractIterator[A] { var these = self def hasNext: Boolean = !these.isEmpty def next(): A = if (hasNext) { val result = these.head; these = these.tail; result } else Iterator.empty.next() */ println("--------遍歷方式1 while -----------------") while (iterator.hasNext) { println(iterator.next()) } println("--------遍歷方式2 for -----------------") for (enum <- iterator) { println(enum) // } } }
11.8.3 對案例演示的小結
1) iterator的構建實際是AbstractIterator的一個匿名子類,該子類提供了
def iterator: Iterator[A] = new AbstractIterator[A] { var these = self def hasNext: Boolean = !these.isEmpty def next(): A = if (hasNext) { val result = these.head; these = these.tail; result } else Iterator.empty.next() }
2) 該AbstractIterator子類提供了hasNext,next等方法
3) 因此,我們可以使用while的方式,使用hasNext,next方法變數
11.9 擴展-流Stream
11.9.1 基本說明
stream是一個集合。這個集合,可以用於存放無窮多個元素,但是這無窮個元素並不會一次性生產出來,而是需要用到多大的區間,就會動態的生產,末尾元素遵循lazy規則(即:要使用結果才進行計算)
11.9.2 創建Stream對象
-案例
def numsForm(n: BigInt): Stream[BigInt] = n #:: numsForm(n + 1) val stream1 = numsForm(1)
-說明
1) Stream集合存放的數據類型是BigInt
2) numsForm是自定義的一個函數,函數名是程式員指定的
3) 創建的集合的第一個元素是n,後續元素生成的規則是n+1
4) 後續元素生成的規則是可以程式員指定的
11.9.3 流的應用案例
//創建 Stream def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1) val stream1 = numsForm(1) println(stream1) // //取出第一個元素 println("head=" + stream1.head) // println(stream1.tail) // 當對流執行 tail 操作時,就會生成一個新的數據. println(stream1) //
11.10 擴展-視圖View
11.10.1 基本介紹
Stream的懶載入特性,也可以對其他集合應用view方法來得到類似的效果,具體有如下特點
1) view方法產出一個總是被懶執行的集合
2) view不會緩存數據,每次都要重新計算,比如遍歷View時
11.10.2 應用案例
請找到1-100中,數字倒敘排列和它本身相同的所有數
object boke_demo01 { def main(args: Array[String]): Unit = { def multiple(num: Int): Int = { num } //如果這個數,逆序後和原來數相等,就返回true,否則返回false def eq(i: Int): Boolean = { println("eq 被調用..") i.toString.equals(i.toString.reverse) } //說明: 沒有使用view,常規方式 val viewSquares1 = (1 to 100).filter(eq) println(viewSquares1) //使用view,來完成這個問題,程式中,對集合進行map,filter,reduce,fold... //你並不希望立即執行,而是在使用到結果才執行,則可以使用view來進行優化. val viewSquares2 = (1 to 100).view.filter(eq) println(viewSquares2) //遍歷 for (item <- viewSquares2) { println("item=" + item) } } }
11.11擴展-並行集合
11.11.1基本介紹
1) Scala為了充分使用多核CPU,提供了並行集合(有別於前面的串列集合),用於多核環境的並行計算
2) 主要用到的演算法有:
Divide and conquer: 分治演算法,Scala通過splitters(分解器),combiners(組合器)等抽象層來實現,主要原理是將計算工作分解很多任務,分發給一些處理器去完成,並將它們處理結果合併返回
Work stealin演算法,主要用於任務調度負載均衡(load-balancing),通俗點完成自己的所有任務之後,發現其他人還有活沒幹完,主動(或被安排)幫他人一起乾,這樣達到儘早幹完的目的
11.11.2 應用案例
-parallel(並行)
列印1~5
(1 to 5).foreach(println(_)) println() (1 to 5).par.foreach(println(_))
-查看並行集合中元素訪問的線程
object boke_demo01 { def main(args: Array[String]): Unit = { val result1 = (0 to 100).map { case _ => Thread.currentThread.getName }.distinct val result2 = (0 to 100).par.map { case _ => Thread.currentThread.getName }.distinct println(result1) //非並行 println("--------------------------------------------") println(result2) //並行 } }
11.12 擴展-操作符
11.12.1 基本介紹
這部分內容沒有必要刻意去記憶,語法使用的多了,自然就會熟練的使用,該部分內容瞭解就可以
11.12.2 操作符擴展
1) 如果想在變數名、類名等定義中使用語法關鍵字(保留字),可以配合反引號反引號,比如 val `val` = 23
2) 中置操作符:A操作符B 等同於 A.操作符(B)
3) 後置操作符:A操作符 等同於 A.操作符,如果操作符定義的時候不帶()則調用時不能加括弧
4) 前置操作符:+、-、!、~等操作符A 等同於 A.unary_操作符
5) 賦值操作符:A操作符=B 等同於 A=A 操作符B,比如A +=B 等價 A = A + B
6) 案例演示
object boke_demo01 { def main(args: Array[String]): Unit = { val n1 = 1 val n2 = 2 val r1 = n1 + n2 // 3 val r2 = n1.+(n2) // 3 看Int的源碼即可說明 val monster = new Monster monster + 10 monster.+(10) println("monster.money=" + monster.money) // 20 println(monster ++) println(monster.++) println("monster.money=" + monster.money) // 22 !monster println("monster.money=" + monster.money) // -22 } } class Monster { var money: Int = 0 //對操作符進行重載 (中置操作符) def +(n: Int): Unit = { this.money += n } //對操作符進行重載(後置操作符) def ++(): Unit = { this.money += 1 } //對操作符進行重載(前置操作符,一元運算符) def unary_!(): Unit = { this.money = -this.money } }