所谓的函数式编程指定就是方法的参数列表可以接收函数对象.
例如: add(10, 20)就不是函数式编程, 而add(函数对象)这种格式就叫函数式编程.
我们将来编写Spark/Flink的大量业务代码时, 都会使用到函数式编程。
遍历(foreach)
采用foreach来遍历集合, 可以让代码看起来更简洁, 更优雅.
格式:
def foreach(f:(A) => Unit): Unit
//简写形式
def foreach(函数)
说明 :
代码示例:
package test9
object Test9 {
def main(args: Array[String]): Unit = {
val list = List(1, 2, 3, 4)
list.foreach((x: Int) => {
print(x + " ")
})
println()
list.foreach(x => print(x + " "))
}
}
简化函数定义
上述案例函数定义有点啰嗦,我们有更简洁的写法。可以通过如下两种方式来简化函数定义:
方式一: 通过类型推断来简化函数定义.
解释:因为使用foreach来迭代列表,而列表中的每个元素类型是确定的, 所以我们可以通过类型推断让Scala程序来自动推断出来集合中每个元素参数的类型, 即: 在我们创建函数时,可以省略其参数列表的类型.
方式二: 通过下划线来简化函数定义.
解释:当函数参数,只在函数体中出现一次,而且函数体没有嵌套调用时,可以使用下划线来简化函数定义.
如果方法参数是函数,如果出现了下划线,scala编译器会自动将代码封装到一个函数中
参数列表也是由scala编译器自动处理
代码示例:
需求:
有一个列表,包含元素1,2,3,4,请使用foreach方法遍历打印每个元素.
使用类型推断来简化函数定义.
使用下划线来简化函数定义
package test10
import scala.Console.println
object Test1 {
def main(args: Array[String]): Unit = {
val list = List(1, 2, 3, 4)
list.foreach((x: Int) => print(x + " "))
println()
//使用类型推断来简化函数定义
list.foreach(x => print(x + " "))
println()
//使用下划线来简化函数定义
list.foreach(print(_))
}
}
映射(map)
集合的映射操作是指将一种数据类型转换为另外一种数据类型的过程
, 它是在进行数据计算的时候, 甚至将来在编写Spark/Flink程序时用得最多的操作,也是我们必须要掌握的.
例如: 把List[Int]转换成List[String].
代码示例:
package test10
object Test2 {
def main(args: Array[String]): Unit = {
val list1 = List(1, 2, 3, 4)
//普通写法
val list2 = list1.map((x: Int) => "*" * x)
println(s"list2 : $list2")
//类型推断
val list3 = list1.map(x => "*" * x)
println(s"list3 : $list3")
//通过下滑线实现
val list4 = list1.map("*" * _)
println(s"list4 : $list4")
}
}
扁平化
flatten可以把嵌套的结构展开。当有一个集合的集合,然后你想对这些集合的所有元素进行操作时,就会用到 flatten。如果一个集合里存放的是元组,则没法压平,只能压平集合。flatten比较简单,没有参数。用的多是跟他名字很像的flatmap。
val arr = Array("hello h1 h2", "nihao zs lisi")
// Array[String] = Array(hello h1 h2, nihao zs lisi)
arr.map(_.split(" "))
//Array[Array[String]] = Array(Array(hello, h1, h2), Array(nihao, zs, lisi))
arr.map(_.split(" ")).flatten
// Array[String] = Array(hello, h1, h2, nihao, zs, lisi)
扁平化映射可以理解为先map,然后再flatten, 它也是将来用得非常多的操作,也是必须要掌握的, 如图:
map是将列表中的元素转换为一个List
flatten再将整个列表进行扁平化
代码示例:
package test10
object Test3 {
def main(args: Array[String]): Unit = {
val list1 = List("hadoop hive spark flink flume", "kudu hbase sqoop storm")
//方式一通过map,flatten实现
val list2 = list1.map(_.split(" "))
val list3 = list2.flatten
println(s"list3 : $list3")
//方式二通过flatMap实现
val list4 = list1.flatMap(_.split(" "))
println(s"list4 : $list4")
}
}
过滤(filter)
过滤指的是过滤出(筛选出)符合一定条件的元素.
格式:
def filter(f:(A) => Boolean): TraversableOnce[A]
//简写形式:
def filter(f:(A) => 筛选条件)
执行过程
代码示例:
package test10
object Test4 {
def main(args: Array[String]): Unit = {
val list1 = (1 to 9).toList
println(s"list1 : $list1")
val list2 = list1.filter(_ % 2 == 0)
println(s"list2 : $list2")
}
}
排序
在scala集合中,可以使用以下三种方式来进行排序:
默认排序
所谓的默认排序指的是对列表元素按照升序进行排列. 如果需要降序排列, 则升序后, 再通过reverse实现.
代码示例:
package test10
object Test5 {
def main(args: Array[String]): Unit = {
val list1 = List(1, 3, 8, 4, 9, 5)
val list2 = list1.sorted
println(s"list1 : $list1")
println(s"list2 : $list2")
val list3 = list2.reverse
println(s"list3 : $list3")
}
}
指定字段排序
所谓的指定字段排序是指对列表元素根据传入的函数转换后,再进行排序.
例如: 根据列表List("01 hadoop", "02 flume")的 字母进行排序.
格式:
def sortBy[B](f:(A) => B): List[A]
//简写形式:
def sortBy(函数对象)
说明:
代码示例:
package test10
object Test6 {
def main(args: Array[String]): Unit = {
val list1 = List(" 01 hadoop", " 02 flume", " 03 hive", " 04 spark")
//普通写法
// val list2 = list1.sortBy(x => x.split(" ")(1))
//简写
val list2 = list1.sortBy(_.split(" ")(1))
println(s"list2 : $list2")
}
}
自定义排序(sortWith)
所谓的自定义排序指的是根据一个自定义的函数(规则)来进行排序.
格式:
def sortWith(f: (A, A) => Boolean): List[A]
//简写形式:
def sortWith(函数对象: 表示自定义的比较规则)
说明:
代码示例:
package test10
object Test7 {
def main(args: Array[String]): Unit = {
val list1 = List(2, 3, 1, 6, 4, 5)
var list2 = list1.sortWith((x, y) => x > y)
println(s"list2 : $list2")
//简写
//第一个下滑线相当于上面的x
//第二个下滑线相当于上面的y
list2 = list1.sortWith(_ > _)
println(s"list2 : $list2")
}
}
分组(groupBy)
分组指的是将数据按照指定条件进行分组, 从而方便我们对数据进行统计分析
格式:
def groupBy[K](f:(A) => K): Map[K, List[A]]
//简写形式:
def groupBy(f:(A) => 具体的分组代码)
说明:
执行过程:
代码示例:
package test10
object Test8 {
def main(args: Array[String]): Unit = {
val list1 = List("刘德华" -> "男", "刘亦菲" -> "女", "胡歌" -> "男")
//普通写法
// val list2 = list1.groupBy(x => x._2)
//简写
val list2 = list1.groupBy(_._2)
println(s"list2 : $list2")
//统计不同性别的学生人数
val list3 = list2.map(x => x._1 -> x._2.size)
println(s"list3 : $list3")
}
}
聚合操作
所谓的聚合操作指的是将一个列表中的数据合并为一个. 这种操作经常用来统计分析中. 常用的聚合操作主要有两个:
reduce: 用来对集合元素进行聚合计算
fold: 用来对集合元素进行折叠计算
聚合(reduce)
reduce表示将列表传入一个函数进行聚合计算.
格式:
def reduce[A 1 >: A](op:(A 1 , A 1 ) ⇒ A 1 ): A 1
//简写形式:
def reduce(op:(A 1 , A 1 ) ⇒ A 1 )
说明:
执行过程
注意:
代码示例:
package test10
object Test9 {
def main(args: Array[String]): Unit = {
val list = (1 to 10).toList
//普通写法
// val list2 = list.reduce((x, y) => x + y)
//简写
val list2 = list.reduce(_ + _)
val list3 = list.reduceLeft(_ + _)
val list4 = list.reduceRight(_ + _)
println(s"list2 : $list2")
println(s"list3 : $list3")
println(s"list4 : $list4")
}
}
折叠(fold)
fold与reduce很像,只不过多了一个指定初始值参数.
格式
def fold[A 1 >: A](z: A 1 )(op:(A 1 , A 1 ) => A 1 ): A 1
//简写形式:
def fold(初始值)(op:(A 1 , A 1 ) => A 1 )
说明:
注意事项:
fold和foldLet效果一致,表示从左往右计算
foldRight表示从右往左计算
代码示例:
定义一个列表,包含以下元素:1,2,3,4,5,6,7,8,9,10
假设初始化值是100, 使用fold方法计算所有元素的和.
package test10
object Test10 {
def main(args: Array[String]): Unit = {
val list1 = (1 to 10).toList
// 普通写法
//val list2 = list1.fold(100)((x, y) => x + y)
//简写
val list3 = list1.fold(100)(_ + _)
val list4 = list1.foldLeft(100)(_ + _)
val list5 = list1.foldRight(100)(_ + _)
println(s"list3 : $list3")
println(s"list4 : $list4")
println(s"list5 : $list5")
}
}
案例: 学生成绩单
需求:
定义列表, 记录学生的成绩, 格式为: 姓名, 语文成绩, 数学成绩, 英语成绩, 学生信息如下: ("张三",37,90,100),("李四",90,73,81), ("王五",60,90,76), ("赵六",59,21,72), ("田七",100,100,100)
获取所有语文成绩在 60 分(含)及以上的同学信息.
获取所有学生的总成绩.
按照总成绩降序排列.
打印结果.
代码示例:
package test11
object Test1 {
def main(args: Array[String]): Unit = {
// 1. 定义列表, 记录学生的成绩, 格式为: 姓名, 语文成绩, 数学成绩, 英语成绩
val stulist = List(("张三", 37, 90, 100), ("李四", 90, 73, 81), ("王五", 60, 90, 76), ("赵六", 59, 21, 72), ("田七", 100, 100, 100))
// 2. 获取所有语文成绩在 60 分(含)及以上的同学信息.
val chineseList = stulist.filter(_._2 >= 60)
// 3. 获取所有学生的总成绩.
val countList = stulist.map(x => x._1 -> (x._2 + x._3 + x._4))
// 4. 按照总成绩降序排列.
val sortList1 = countList.sortBy(_._2).reverse
//也可以通过sortWith实现.
val sortList2 = countList.sortWith((x, y) => x._2 > y._2)
println(s"语文成绩及格的学生信息 :$chineseList")
println(s"所有学生及其总成绩 :$countList")
println(s"总成绩降序排列 : $sortList1")
println(s"总成绩降序排列 :$sortList2")
}
}