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集合就会进行无限循环
}
}