一、集合元素映射
1.1 map
1.1.1 实际需求
将List(1,2,3,4,5)
中的每一个元素扩大两倍后放入一个新的List
中返回
1.1.2 代码实现
def testMap(): List[Int] = {
List(1, 2, 3, 4, 5).map((x: Int) => x * 2)
}
1.1.3 逻辑分析
map
将集合的每一个元素通过指定的函数映射成一个新的结果集- 以
List
为例map
长这样map[B](f: A => B): List[B]
map
函数任何集合都有A
、B
为泛型map
函数需要接收一个函数参数,且函数的格式是接收一个参数返回一个值List[B]
为map
函数的返回值,不同集合类型的map
返回值不同
- 对于匿名函数的函数体,只有一句代码时
{}
可以省略 - 简化参数传递
- 如上述代码对于高阶函数(可以接收函数的函数)传参使用匿名函数
- 若作用的集合元素为同一个类型匿名函数的参数列表可以不写参数类型(编译器进行类型推导)
- 若匿名函数参数为一个可以不写小括号,总和
2,3
可以简化为map(x => x * 2)
- 对于上述这种简单逻辑一个参数还可以通过通配符简化为
map(_ * 2)
,其中_
指代集合的所有元素
1.1.4 练习
将List("aaa","bbb","ccc")
中的元素转化为大写
List("aaa","bbb","ccc").map(_.toUpperCase)
1.2 flatMap
1.2.1 实际需求
将List(List(1,3,5),List(2,4,6))
集合的每个元素里的元素扩大两倍返回到一个集合即List(2,6,10,4,8,12)
1.2.2 代码实现
def testFlatMap(): List[Int] = {
List(List(1, 3, 5), List(2, 4, 6)).flatMap(_.map(_ * 2))
}
1.2.3 逻辑分析
flat
即扁平化;flatMap
将集合中的每个元素的子元素映射到指定函数并返回新的集合- 以
List
为例flatMap
长这样flatMap[B](f: A => IterableOnce[B]): List[B]
flatMap
需要一个函数为参数,且参数要求为一个参数返回一个IterableOnce
,这是所有集合的父类,即返回一个集合- 执行流程为取出集合的每一个元素(也是集合类型),对其元素进行操作后的结果放入一个新的集合中,且对每个元素的处理结果都会放入同一个集合实现扁平化处理
1.2.4 练习
将List("aaa","bbb","ccc")
中的元素转化为一个个大写字母(字符串在scala
中相当于一个Array[Char]
)
List("aaa","bbb","ccc").flatMap(_.toUpperCase)
二、集合元素归约
2.1 reduce
2.1.1 实际需求
实现对List(1,2,3,4,5)
的求和
2.1.2 代码实现
def testReduce(): Int = {
List(1, 2, 3, 4, 5).reduce(_ + _)
}
2.1.3 逻辑分析
- 上述代码为简化后的形式,可以写成
reduce((x,y)=> x + y )
- 执行流程
- x=1,y=2 => x+y=3
- x=3,y=3 => x+y=6
- x=6,y=4 => x+y=10
- x=10,y=5 => x+y=15
- 一句话总结:
reduce
先从集合中取出两个值(最前面)赋值为x,y
后进行运算,将运算的结果赋值给下一次的x
,顺序取出集合的下一个元素赋值给y
,依次执行到集合最后一个元素后返回最终结果
def reduce[B >: A](op: (B, B) => B): B = reduceLeft(op)
redcue
底层调用的是reduceLeft
,长这样reduceLeft[B >: A](op: (B, A) => B): B
- 二者区别在于对参数的要求上,
redcue
对参数要求更为严格 - 有左就有右
reduceRight[B >: A](op: (A, B) => B): B
,与reduceLeft
唯一的区别在于执行的方向不同,``reduceRight`从集合尾部开始取值,在一些特定的场合会有不错的效果。
1.2.4 练习
找出List(1, 7, 5, 4, 3)
中的最大值
List(1, 7, 5, 4, 3).reduce((x, y)=>if(x > y) x else y)
2.2 fold
2.2.1 实际需求
统计"哈哈嘿嘿哈哈呵呵哈吼吼哈嘻嘻"每个汉字出现的个数并将结果保存到Map
中
2.2.2 代码实现
def testFold(): mutable.Map[String, Int] = {
"哈哈嘿嘿哈哈呵呵哈吼吼哈嘻嘻".foldLeft(mutable.Map[String, Int]())((x: mutable.Map[String, Int], y) => {
x.put(y.toString, x.getOrElse(y.toString, 0) + 1)
x
})
}
2.2.3 逻辑分析
-
逻辑类似
reduce
-
fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
-
(z: B)为初始参数
-
区别
reduce
在于fold
有初始参数,会将初始参数赋值给第一个参数,从集合顺序取出一个元素赋值给第二个元素,之后逻辑和reduce
一个 -
同样有左右之分
foldLeft
、foldRight
,只是执行的顺序不同,区别于reduce
同样是foldLeft
对参数类型要求相对宽松 -
从
foldRight
源码可以看出和foldLeft
关系def foldRight[B](z: B)(op: (A, B) => B): B = reversed.foldLeft(z)((b, a) => op(a, b))
-
-
对2.2.2代码分析
- 初始化一个可变
Map
(HashMap
)作为初始参数,用于保存结果,记作map
fold
将map
传给x,从集合顺序取出一个元素即’哈’赋值给y- 若此时的y在
map
第一次出现则put值为1,否则put值为原值加1 - 将map返回作为下一次的x,重复第1步
- 初始化一个可变
2.3 scan
相当于fold
操作,只不过把每次处理数据的中间值保存到一个集合中
def scan[B >: A](z: B)(op: (B, B) => B): CC[B] = scanLeft(z)(op)
测试
def testScan(): Unit = {
//scan 相当于fold,返回存储中间结果的集合
val value = List(1, 2, 3, 4, 5).scan(1)(_ + _)
println(value)
}
//结果
List(2, 6, 10, 4, 8, 12)
三、集合元素合并
3.1 zip
3.1.1 实际需求
将List("zs","ls","ww"),List(18,27,23)
对应元素组合到一起形成一个新的集合
3.1.2 代码实现
def testZip(): List[(String, Int)] = {
List("zs", "ls", "ww").zip(List(18, 27, 23))
}
//运行结果
List((zs,18), (ls,27), (ww,23))
3.1.3 逻辑分析
- 顺序取出两个集合元素封装成为元组后存储到新的集合
- 若两个集合长度不对等,按长度小的集合为标准返回,忽略多出的元素
zip
通常称为拉链
3.2 groupBy
3.2.1 实际需求
对List(("hadoop", 1), ("spark", 1), ("hadoop", 1), ("spark", 1), ("kafka", 1), ("flume", 1))
按照元素(元组)的第一个元素进行分组
3.2.2 代码实现
def testGroupBy(): Map[String, List[(String, Int)]] = {
List(("hadoop", 1), ("spark", 1), ("hadoop", 1), ("spark", 1), ("kafka", 1), ("flume", 1)).groupBy(_._1)
}
//运行结果
HashMap(kafka -> List((kafka,1)), hadoop -> List((hadoop,1), (hadoop,1)), spark -> List((spark,1), (spark,1)), flume -> List((flume,1)))
解释:_._1
第一个_
指代集合元素,其中该元素为元组因此._1
表示取元组的第一个元素
3.2.3 逻辑解释
- 以本题为例,
groupBy
长这样groupBy[K](f: A => K): immutable.Map[K, C]
- 执行流程
- 将
f
的返回值作为Map
的key,将相同返回值的集合元素封装成集合作为value - 注意是将集合元素原封不动作为元素进行封装
- 虽然返回的是不可变
Map
但底层使用的是可变Map
,处理完后将可变集合元素赋值给不可变返回
- 将
3.3 filter
3.3.1 实际需求
过滤List(1,2,3,4,5)
所有偶数(奇数)
3.3.2 代码实现
def testFilter(): List[Int] = {
List(1, 2, 3, 4, 5).filter(_ % 2 == 0)
}
3.3.3 逻辑分析
filter
长这样def filter(p: A => Boolean): Iterator[A]
- 接收一个函数作为参数,该函数需要返回一个布尔值
- 若为真则将该集合元素保存到新的集合,反之不然
3.4 find
3.4.1 功能
返回集合第一个满足条件的元素封装成Some
若匹配不到返回None
3.4.2 案例
def testFind(): Option[Int] ={
List(1, 2, 3, 4, 5).find(_ % 2 == 0)
}
//运行结果
Some(2)
3.5 foreach
3.5.1 功能
得到集合的每一个元素,没有返回值,通常搭配print
系列函数打印集合元素
3.5.2 案例
def testForEach(): Unit = {
List(1, 2, 3, 4, 5).foreach(println)
}
3.6 take 系列
3.6.1 take
1.功能
获取集合前n个元素,通常搭配排序用于取出top n
2.案例
def testTake(): Unit = {
List(1, 2, 3, 4, 5).take(2).foreach(println)
}
3.6.2 takeRight
1.功能
获取集合后n个元素,在特定场合有奇效
2.案例
def testTakeRight(): Unit = {
List(1, 2, 3, 4, 5).takeRight(2).foreach(println)
}
3.6.3 takeWhile
1.功能
逐个匹配满足条件的元素,直到不匹配为止
2.案例
def testTakeWhile(): Unit = {
List(1, 2, 3, 4, 5).takeWhile(_ % 2 == 0).foreach(println)
}
//结果为(),因为第一个元素就不满足条件
3.7 drop 系列
同take
有drop(n:Int)
、dropRight(n:Int)
、dropWhile(p: A => Boolean)
3.8 partition
3.8.1 功能
partition(p: A => Boolean)
对集合元素进行分区,根据函数返回的布尔值进行分区,因此只能分两个区,结果封装成一个元组。
3.8.2 案例
def testPartition(): (List[Int], List[Int]) = {
List(1, 2, 3, 4, 5).partition(_ % 2 == 0)
}
3.9 head/tail
3.9.1 功能
返回集合的头和尾元素,其中对于尾的定义:在scala中规定不是头就是尾,即头和尾可以构成原集合
注意:头尾概念只有LinerSeq
集合才有
3.9.2 案例
def testHeadTail(): Unit = {
println(List(1, 2, 3, 4, 5).head)
println(List(1, 2, 3, 4, 5).tail)
}
//运行结果
1
List(2, 3, 4, 5)
四、集合元素排序
4.1 sorted
4.1.1 功能
排序,按照默认规则排序(数字升序,字符串字典序),不能排序自定义对象
4.1.2 案例
def testSorted(): Unit = {
println(List(1, 3, 2, 5, 4).sorted)
}
4.2 sortBy
4.2.1 功能
排序,需要传入一个函数为参数,该函数返回一个值,根据这个值对元素进行排序
4.2.2 案例
def testSortBy(): Unit = {
println(List(1, -3, 2, -5, 4).sortBy(_.abs))
}
//运行结果
List(1, 2, -3, 4, -5)
解释:按照元素的绝对值大小进行排序
4.3 sortWith
4.3.1 功能
排序,这个更像java的compareTo()
方法,接受一个函数作为参数,该参数需要接收两个集合元素,返回一个布尔值
4.3.2 案例
def testSortWith(): Unit = {
println(List(1, -3, 2, -5, 4).sortWith((x, y) => x > y))
}
//运行结果
List(4, 2, 1, -3, -5)
五、实现 WordCount
5.1 需求
统计List("soft liu hello ddd", "soft liu aaa aaa ccc ddd uuu", "liu hello","ddd ccc")
单词出现的个数,先按出现次数排序,次数一样按照单词字典序排序
5.2 分析
5.2.1 通过foldLeft实现
- 将集合元素进行扁平化处理,即拆分单词封装到新的集合
- 将元素一次添加到
Map
中 - 将
Map
强转成支持排序的集合 - 排序
5.2.2 通过groupBy实现
- 将集合元素进行扁平化处理,即拆分单词封装到新的集合
- 按照单词进行分组
- 分组内部进行统计
- 排序
5.3 实现
private val list = List("soft863 liu hello ddd", "soft863 liu aaa aaa ccc ddd uuu", "liu hello", "ddd ccc")
5.3.1 foldLeft
def byFoldLeft(): List[(String, Int)] = {
list
.flatMap(_.split(" "))
.foldLeft(mutable.Map[String, Int]())((map: mutable.Map[String, Int], word) => {
map.put(word, map.getOrElse(word, 0) + 1)
map
})
.toList
.sortWith((t1, t2) => if (t1._2 == t2._2) t1._1 < t2._1 else t1._2 > t2._2)
}
5.3.2 groupBy
def byGroupBy(): List[(String, Int)] = {
list
.flatMap(_.split(" "))
.groupBy(x => x)
.view.mapValues(_.length)
.toList
.sortWith((t1, t2) => if (t1._2 == t2._2) t1._1 < t2._1 else t1._2 > t2._2)
}
5.4 拓展
统计List(("soft liu hello ddd", 4), ("soft liu aaa aaa ccc ddd uuu", 2), ("liu hello", 2), ("ddd ccc", 3))
单词出现的个数,其中元组的第二个元素表示该元组里单词出现的个数
def wordCount(): List[(String, Int)] = {
List(("soft liu hello ddd", 4), ("soft liu aaa aaa ccc ddd uuu", 2), ("liu hello", 2), ("ddd ccc", 3))
.flatMap(t => {
t._1.split(" ").map((_, t._2))
})
.groupBy(_._1)
.view.mapValues(x => x.reduce((x, y) => (x._1, x._2 + y._2))._2)
.toList
.sortWith((t1, t2) => if (t1._2 == t2._2) t1._1 < t2._1 else t1._2 > t2._2)
}