11.1集合元素的映射-map映射操作
11.1.1看一個實際需求
要求:請將List(3,5,7) 中的所有元素都 * 2 ,將其結果放到一個新的集合中返回,即返回一個新的List(6,10,14), 請編寫程序實現.
11.1.2使用傳統的方法解決
//傳統寫法
val list1 = List(3, 5, 7)
var list2 = ListBuffer[Int]()
for (item <- list1) { //遍歷
list2.append(item * 2)
}
println(list2)
11.1.3傳統方法的分析
傳統方法優點分析
1.簡單,很好理解
傳統方法缺點分析
1.不夠簡潔,不利於模塊化編程
2.如果我們需要對返回的集合進行下一步處理,不方便
11.2map映射操作
上面提出的問題,其實就是一個關於集合元素映射操作的問題。
在Scala中可以通過map映射操作來解決:將集合中的每一個元素通過指定功能(函數)映射(轉換)成新的結果集合這裏其實就是所謂的將函數作爲參數傳遞給另外一個函數,這是函數式編程的特點
以HashSet爲例說明
1)def map[B](f: (A) ⇒ B): HashSet[B] //map函數的簽名
2)這個就是map映射函數集合類型都有
3)[B] 是泛型
4)map 是一個高階函數(可以接受一個函數的函數,就是高階函數),可以接收 函數 f: (A) => B 後面詳解(先簡單介紹下.)
5)HashSet[B] 就是返回的新的集合
6)對於其他的集合比如(List,Array,ArrayBuffer…) 也可以進行map
11.2.1使用map來解決
val list1 = List(3, 5, 7)
def f1(n1: Int): Int = {
2 * n1
}
val list2 = list1.map(f1)
println(list2)
11.2.2使用scala模擬map的映射操作
object MapOperTheory02 {
def main(args: Array[String]): Unit = {
val list = new MyList
val newList = list.map(f1)
println("newList=" + newList) //List(2,4,6)
}
def f1(n:Int): Int = {
2 * n
}
}
class MyList {
val list1 = List(1,2,3)
var list2 = List[Int]() //空
//map就是高階函數,可以接受 f:(Int) => Int
def map(f:(Int) => Int): List[Int] = {
//1. 遍歷list1
//2. 當遍歷出來一個元素後,就傳遞給f
//3. 將得到結果,放入到新的集合中
for (item<-list1) {
//過濾
list2 = list2 :+ f(item)
}
list2
}
}
11.2.3練習
請將 val names = List(“Alice”, “Bob”, “Nick”) 中的所有單詞,全部轉成字母大寫,返回到新的List集合中
object Exercise01 {
def main(args: Array[String]): Unit = {
/*
val names = List("Alice", "Bob", "Nick")
// 思路分析
//1. 考慮使用map映射
//2. 應該寫一個函數,可以將遍歷出的元素,轉成大寫
*/
val names = List("Alice", "Bob", "Nick")
val res = names.map(toUpper)
println("res=" + res)
}
def toUpper(s:String): String = {
s.toUpperCase
}
}
11.2.4flatmap映射:flat即壓扁,壓平,扁平化映射
flatmap的基本介紹
flatmap:flat即壓扁,壓平,扁平化,效果就是將集合中的每個元素的子元素映射到某個函數並返回新的集合。
案例演示
object FlatMapDemo01 {
def main(args: Array[String]): Unit = {
val names = List("Alice", "Bob", "Nick")
val res = names.flatMap(f1)
println("res=" + res)
}
//將一個String 轉成了大寫
def f1(s:String): String = {
println("s=" + s)
s.toUpperCase
}
}
使用scala模擬了扁平化的操作
object MapOperTheory02 {
def main(args: Array[String]): Unit = {
val list = new MyList
val newList = list.map(f1)
println("newList=" + newList) //List(2,4,6)
//val names = List("Alice", "Bob", "Nick")
val newList2 = list.flatMap(toUpper)
println("newList2=" + newList2)
}
def f1(n: Int): Int = {
2 * n
}
def toUpper(str:String): String = {
str.toUpperCase
}
}
class MyList {
val list1 = List(1, 2, 3)
var list2 = List[Int]() //空
val names = List("Alice", "Bob", "Nick")
var list3 = List[Char]() //空
//map就是高階函數,可以接受 f:(Int) => Int
def map(f: (Int) => Int): List[Int] = {
//1. 遍歷list1
//2. 當遍歷出來一個元素後,就傳遞給f
//3. 將得到結果,放入到新的集合中
for (item <- list1) {
//過濾
list2 = list2 :+ f(item)
}
list2
}
//寫一個map2, 模擬一段flatMap
def flatMap(f: (String) => String): List[Char] = {
for (item <- names) { // 遍歷names.length-1
val temp = f(item)
//對temp扁平化
for (i <- temp) {
list3 = list3 :+ i
}
}
list3
}
}
11.3集合元素的過濾-filter
filter:將符合要求的數據(篩選)放置到新的集合中
應用案例:將 val names = List(“Alice”, “Bob”, “Nick”) 集合中首字母爲’A’的篩選到新的集合。
如果這個使用傳統的方式,如何完成? =》 遍歷+if判斷
案例演示
object FilterDemo {
def main(args: Array[String]): Unit = {
val names = List("Alice", "Bob", "Nick")
def startA(s:String): Boolean = {
//如果s是以'A' 開頭,則返回true
s.startsWith("A")
}
val names2 = names.filter(startA) // 如果startA 返回一個true,則加入到新集合中,否則,過濾
println("names=" + names2)
}
}
11.4化簡
object ReduceLeftDemo {
def main(args: Array[String]): Unit = {
val list = List(1, 20, 30, 4, 5)
def sum(n1: Int, n2: Int): Int = {
n1 + n2
}
//使用reduceLeft 求和
//說明和介紹
//1. reduceLeft 左化簡
//2. reduceLeft 可以接收一個函數
// def reduceLeft[B >: A](@deprecatedName('f) op: (B, A) => B): B
//3. reduceLeft 接收的函數 op: (B, A) => B 接收兩個參數,運算後將結果返回給下一次reduceLeft的第一個參數(左邊參數)
//4. 運行的規則 (1) 先從list 從左取出兩個參數,(2) 運算後 將結果返回左邊的參數,進行下次運算 (3)反覆執行,直到list的所有的元素遍歷完畢
//具體 List(1, 20, 30, 4, 5)
// (((1 + 20 ) + 30) + 4) + 5 = 60
val res = list.reduceLeft(sum)
println("res=" + res)
}
}
第二個題
求出List的最小值
object ReduceExercise {
def main(args: Array[String]): Unit = {
/*
使用化簡的方法求出 List(3,4,2,7,5) 最小的值
//2min
*/
println(List(3,4,2,7,5).reduceLeft(myMin)) // 2
println(List(3,4,2,7,5).reduceRight(myMin)) // 2
}
def myMin(n1: Int, n2: Int): Int = {
if (n1 > n2) n2 else n1
}
}
11.5摺疊
11.5.1基本介紹
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.foldLeftB(op)
大家可以看到. reduceLeft就是調用的foldLeftB,並且是默認從集合的head元素開始操作的。
2)相關函數:fold,foldLeft,foldRight,可以參考reduce的相關方法理解
11.5.2案例
看下面代碼看看輸出什麼,並分析原因.
// 摺疊
val list = List(1, 2, 3, 4)
def minus( num1 : Int, num2 : Int ): Int = {
num1 - num2
}
//等價 List(5,1,2,3,4) 進行操作
// (((5-1) – 2) – 3) – 4 = -5
println(list.foldLeft(5)(minus)) // 函數的柯里化
//等價 List(1,2,3,4,5) 進行操作
// 1-(2- (3- (4-5))) = 3
println(list.foldRight(5)(minus)) //
11.5.3foldLeft和foldRight 縮寫方法分別是:/:和:\
val list4 = List(1, 9, 2, 8)
def minus(num1: Int, num2: Int): Int = { //函數
num1 - num2
}
var i6 = (1 /: list4) (minus) // 等價於 list4.foldLeft(1)(minus)
println(i6) // 輸出? -19
i6 = (100 /: list4) (minus) // 等價 list4.foldLeft(100)(minus)
println(i6) // 輸出?
// (1, 9, 2, 8,10) => 1-(9-(2 -(8 – 10))) = -4
i6 = (list4 :\ 10) (minus) // -4 等價 list4.foldRight(10)(minus)
println(i6) // 輸出?
11.5.4掃描
11.5.5基本介紹
掃描,即對某個集合的所有元素做fold操作,但是會把產生的所有中間結果放置於一個集合中保存
11.5.6應用實例
object ScanDemo01 {
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)
// 說明(1 to 5).scanLeft(5)(minus) 執行流程
// 1.先將 5 放到 被操作的集合的左邊 (5,1,2,3,4,5)
// 2.將第一個數保存到新的集合
// 3.開始進行左摺疊的操作[左摺疊如何操作前面說過],並把每次返回的結果放入到新的集合中
// 4.最後返回新的集合 (5,1,2,3,4,5) => (5,4, 2,-1,-5,-10)
val i8 = (1 to 5).scanLeft(5)(minus) //
println(i8)
// (1,2,3,4,5) => (3,-2,4,-1,5)
val i10 = (1 to 4).scanRight(5)(minus)
println("i10=" + i10) //
//看一個案例
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) //
println(i9)
}
}
11.5.7課堂練習
請寫出下面的運行結果:
結果是 (3,3,6,18)
11.6集合綜合應用案例
11.6.1課堂練習1
val sentence = “AAAAAAAAAABBBBBBBBCCCCCDDDDDDD”
將sentence 中各個字符,通過foldLeft存放到 一個ArrayBuffer中
目的:理解flodLeft的用法.
val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"
def putArry( arr : ArrayBuffer[Char], c : Char ): ArrayBuffer[Char] = {
arr.append(c)
arr
}
//創建val arr = ArrayBuffer[Char](), 作爲第一個參數傳入
val arr = ArrayBuffer[Char]()
sentence.foldLeft(arr)(putArry)
11.6.2課堂練習2
val sentence = “AAAAAAAAAABBBBBBBBCCCCCDDDDDDD”
使用映射集合,統計一句話中,各個字母出現的次數
提示:MapChar, Int , 這裏使用map合理的
1)看看java如何實現
String sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD";
Map<Character, Integer> charCountMap =
new HashMap<Character, Integer>();
char[] cs = sentence.toCharArray();
for ( char c : cs ) {
if ( charCountMap.containsKey(c) ) {
Integer count = charCountMap.get(c);
charCountMap.put(c, count + 1);
} else {
charCountMap.put(c, 1);}}
System.out.println(charCountMap); //ok
2)使用scala實現
題目的要求:
val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"使用映射集合,統計一句話中,各個字母出現的次數提示:MapChar, Int
代碼案例
import scala.collection.mutable
object Exercise03 {
def main(args: Array[String]): Unit = {
/*
val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"
使用映射集合,統計一句話中,各個字母出現的次數
提示:Map[Char, Int]()
需求: 使用映射集合,統計一句話中,各個字母出現的次數
分析思路
1. 使用左摺疊, 確定傳入的第一個參數(變量),使用Map[Char,Int]
2. 寫一個函數來處理charCount(Map[Char,Int],c:Char) : Map[Char,Int] = {}
3. charCount 函數內部,使用對map 的操作 map + (key->值)
代碼實現
*/
val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"
val map = mutable.Map[Char, Int]()
def charCount(map: mutable.Map[Char, Int], c: Char): mutable.Map[Char, Int] = {
map + (c -> (map.getOrElse(c, 0) + 1))
}
val map2 = sentence.foldLeft(map)(charCount)
println("map2=" + map2)
//第二種寫法
// val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"
//
//
// def charCount(map: mutable.Map[Char, Int], c: Char): mutable.Map[Char, Int] = {
// map + (c -> (map.getOrElse(c, 0) + 1))
// }
//
// val map2 = sentence.foldLeft(mutable.Map[Char, Int]())(charCount)
//
// println("map2=" + map2)
}
}
class Dog {
def say(d:Dog): Unit = {
}
}
11.7擴展-拉鍊(合併)
11.7.1基本介紹
在開發中,當我們需要將兩個集合進行 對偶元組合並,可以使用拉鍊。
11.7.2應用實例
object ZipDemo {
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) //(1,4),(2,5),(3,6)
//遍歷list3 ((1,4),(2,5),(3,6))
for (item <- list3) {
println(item._1 + " " + item._2)
}
}
}
11.7.3使用zip的注意事項
1)拉鍊的本質就是兩個集合的合併操作,合併後每個元素是一個 對偶元組。
操作的規則下圖:
3)如果兩個集合個數不對應,會造成數據丟失。
4)集合不限於List, 也可以是其它集合比如 Array
5)如果要取出合併後的各個對偶元組的數據,可以遍歷
//遍歷list3 ((1,4),(2,5),(3,6))
for (item <- list3) {
println(item._1 + " " + item._2)
}
11.8擴展-迭代器
當我們需要在while中去遍歷一個集合時,就需要使用到迭代器.
11.8.1基本說明
通過iterator方法從集合獲得一個迭代器,通過while循環和for表達式對集合進行遍歷.(學習使用迭代器來遍歷)
11.8.2應用案例
object IteratorDemo {
def main(args: Array[String]): Unit = {
val iterator = List(1, 2, 3, 4, 5).iterator // 得到迭代器
println("--------遍歷方式1 -----------------")
while (iterator.hasNext) { //判斷是否還有下一個
println(iterator.next())
}
println("--------遍歷方式2 for -----------------")
val iterator2 = List(11, 22, 33, 44, 55).iterator // 得到迭代器
for(enum <- iterator2) { //迭代器也可以在for循環中使用
println(enum) //
}
}
}
11.8.3應用案例小結
- iterator 的構建實際是 AbstractIterator 的一個匿名子類,該子類提供了
/*
def iterator: Iterator[A] = new AbstractIterator[A] {
var these = self
def hasNext: Boolean = !these.isEmpty
def next(): A =
*/
3)該AbstractIterator 子類提供了 hasNext next 等方法.
4)因此,我們可以使用 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)
11.9.3說明
1)Stream 集合存放的數據類型是BigInt
2)numsForm 是自定義的一個函數,函數名是程序員指定的。
3)創建的集合的第一個元素是 n , 後續元素生成的規則是 n + 1
4)後續元素生成的規則是可以程序員指定的 ,比如 numsForm( n * 4)…
11.9.4代碼演示和注意事項
object StreamDemo01 {
def main(args: Array[String]): Unit = {
//對代碼的說明
/*
1.Stream 集合存放的數據類型是BigInt
2.numsForm 是自定義的一個函數,函數名是程序員指定的。
3.創建的集合的第一個元素是 n , 後續元素生成的規則是 n + 1
4.後續元素生成的規則是可以程序員指定的 ,比如 numsForm( n * 4)...
*/
def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)
val stream1 = numsForm(1) // 1
println("stream1=" + stream1) // (1,?)
//取出第一個元素
println("head=" + stream1.head) //1
println(stream1.tail) // (2,?)
println(stream1) //? (1,2,?)
//注意:如果使用流集合,就不能使用last屬性,如果使用last集合就會進行無限循環
}
}