Scala 编程 -Scala基础
1.课程目标
- 安装Scala编译和运行环境
- 熟悉Scala基本语法以及函数式编程
- 熟练掌握Scala数据结构使用以及集合方法操作
2.scala简介以及安装
- scala概念:面向对象和面向函数的多范式编程语言,scala也是基于jvm的编程语言,并且scala可以和java的语法兼容。
- 面向对象:scala是纯面向对象的语言。举例:1+2 :1 其实对象 2 对象 +方法操作 1.+(2)。在scala的世界中,每一个值都是一个对象,每一个操作都是方法操作
- 面向函数:函数2个基本核心
- 函数:是输入数据到输出数据的映射关系, 比如 f(x)=>x*x 。x在整个映射关系过程中不可变
- 函数其实也是一个对象,函数可以赋值给变量、可以作为方法的参数、作为方法的返回值
- Scala编程特点
- 优雅:scala是一个非常优雅的编程语言,能够非常轻松的实现一些重要的功能
- 语言表达能力极强:一般是java开发的3-5倍
- Scala是spark以及kafka等大数据框架开发语言,scala能够融入大数据生态圈
- scala是基于jvm的编程语言,scala具有可移植性,能够与java语法很好兼容
- Scala编译环境的安装
- 安装步骤
- 下载scala SDK https://www.scala-lang.org/
- 解压缩并且规划安装目录
- 配置环境变量 定位到bin目录
- object HelloWord{
def main(arr:Array[String]):Unit={
println("helloword")
}
}
- 安装步骤
- 基于IDEA 开发scala应用程序
- 安装scala插件 http://plugins.jetbrains.com/plugin/1347-scala
- 注意:scala插件版本最好与IDEA的版本保持一致
- Config--》plugins--》install from Disk
-
/** * object:修饰的是一个对象 * def: 定义方法的关键词 * args: Array[String]:参数 * Unit:返回值类型 */ object HelloWord { def main(args: Array[String]): Unit = { println("helloword") } }
3.scala基础语法
3.1变量
- 格式:
-
语法: val/var 名称:数据类型=值 val 修饰的不可变的变量 相当于java中final修饰的变量(推荐使用val变量) var 修饰的可变变量 val name: String = "itcast" var age: Int = 10 age=20 val name2="itcast" var age2=10 注意:scala也是一种强类型语言,如果省略数据类型,则可以根据值进行推导
-
3.2数据类型
- 整型:Byte Short Int Long Char
- 浮点:Float Double
- 布尔:Boolean
- 字符串:String
- 注意:以上数据类型 都是类 所创建的所有值都是对象
-
//插值器 s代表插值器开始 $age 代表变量 val str2=s"itcast $age" println(str2) //原生态字符串 val str3= """sdfsdfdsfs sfsdfdsfsfd sdfsfds sfssdfdsfds """ val str4= """ |afdsfd |sdfdsf |sfdsf |sdfsdf """.stripMargin println(str3) println(str4)
3.3 操作符
- 算数运算符: + - * / %
- 逻辑运算符:&& || !
- 比较运算符: > < >= <=
- scala中运算符都是方法操作
3.4 逻辑表达式
- if、for、 块表达式
-
表达式 :一般都具有返回值
-
3.4.1 if表达式
-
格式:val 变量名称= if(逻辑运算){ 程序体 }else if(逻辑运算){ 业务逻辑 }else { 业务逻辑 } val score = 90 val isOk = if (score >= 90) "及格" else "不及格" val isValid = if (score > 0) score else "error" val myfeel = if (score > 95) "高兴" else () //相当于 Unit println(isOk) println(isValid) println(myfeel) //注意:1.if可以返回不同的数据类型。变量类型是有if返回值类型推导而来 // 2. 如果if当中没有任何匹配上的值的情况默认返回 ()相当于无返回值情况 Unit java :三目运算符
3.4.2 块表达式
-
概念:使用{}括起来的具有返回值的表达式 就叫做块表达式 val 变量名称={ 具有返回值的表达式} 应用场景:使用在方法内部 可以作为初始化块使用 val height = 10 val width = 100 val area = { //多条语句 height * width //块表达式的返回值 一定是块表达式内最后一条语句 } println(area)
3.4.3 for循环
-
基本语法: for(变量 <- 集合\数组\表达式) { 循环体 } /** * for(变量 <- 集合\数组\表达式) { * 循环体 * } * to: 返回的是集合 是一个闭区间集合 0 1 2 3 * until :返回的是一个集合 是一个开区间集合 */ for (index <- 0 to list.size() - 1) { println(list.get(index)) } println("------------------------") for (index <- 0 until list.size()) { println(list.get(index)) } 注意: index 是不可变的val类型,在每次循环过程中都会创建新的index变量。 *凡是省略val或者var的地方 默认都是val的
-
基本语法: for(变量 <- 集合\数组\表达式 if 条件语句 * ) { 循环体 } for (index <- 0 until list.size() if list.get(index).contains("Hadoop") if list.get(index).contains("storm") ) { println(list.get(index)) }
-
基本语法: for(变量 <- 集合\数组\表达式; 变量 <- 集合\数组\表达式) { 循环体 } //hadoop 内容列表 val hadoopList = new util.ArrayList[String]() hadoopList.add("mapreduce") hadoopList.add("hdfs") //java 内容列表 val javaList = new util.ArrayList[String]() javaList.add("JavaSE") javaList.add("JavaWeb") javaList.add("javaEE") val map = new util.HashMap[String, util.List[String]]() map.put("hadoop", hadoopList) map.put("java", javaList) /** * hadoop * --mapreduce * --hdfs * java * -JavaSE * -JavaWeb * -javaEE */ val keyArray = map.keySet().toArray() //scala中array /** * for(变量 <- 集合\数组\表达式; 变量 <- 集合\数组\表达式) { * 循环体 * } */ for (key <- keyArray if key.equals("java"); list = map.get(key); index <- 0 until list.size() if index>0 ) { println(list.get(index)) }
-
基本语法: val 集合名称=for(变量 <- 集合\数组\表达式) yield 表达式 //作用于集合中的每个元素的 val goods = new util.ArrayList[Int]() goods.add(100) goods.add(50) goods.add(25) //val 集合名称=for(变量 <- 集合\数组\表达式) yield 表达式 val newGoods = for (index <- 0 until goods.size()) yield goods.get(index) * 0.95 for (g <- newGoods) { println(g) }
3.5 方法和函数的定义
3.5.1 函数
-
1.函数是输入数据到输出数据的映射关系 (x:Int)=>{x*x} 2.函数是一个对象 val 变量名称=函数 格式: val 变量名称=(输入数据)=>{输出数据} // 定义求平方函数 //{} 省略原则: 当函数体只有一行代码的时候可以省略 val square = (x: Int) => x * x //函数使用 val result: Int = square(10) println(result) println(square) val square2 = (x: Int) => x * x println(square2) val area = (height: Int, width: Int) => height * width println(area) val cube: Function3[Int, Int, Int, Int] = (x: Int, y: Int, z: Int) => x * y * z println(cube(1,2,3)) 注意: 函数的类型都是FunctionN N代表的就是函数输入参数的个数 N的最大值是22 在scala中凡是 类N 的 ,N的最大值都是22 // 定义求平方函数 //{} 省略原则: 当函数体只有一行代码的时候可以省略 val square: Int => Int = (x: Int) => x * x //函数使用 val result: Int = square(10) println(result) println(square) val square2 = (x: Int) => x * x println(square2) val area: (Int, Int) => Int = (height: Int, width: Int) => height * width println(area) val cube: (Int, Int, Int) => Int = (x: Int, y: Int, z: Int) => x * y * z println(cube(1, 2, 3))
3.5.2 方法定义
-
格式:def 方法名称(参数列表):返回值类型={ 方法体 } /** * 格式:def 方法名称(参数列表):返回值类型={ * 方法体 * } */ object Scala_day01_method { //定义求平方的方法 def square(x: Int): Int = { x * x //省略return操作 将方法体的最后一个表达式作为了方法的返回值 } // 省略了返回值类型 以及{} def square2(x: Int) = x * x def square3(x: Int) = { println(x) x * x } //当递归调用的时候 不能省略返回值 // 求阶乘 5*4*3*2*1 def jieCheng(x: Int): Int = { if (x <= 1) 1 else x * jieCheng(x - 1) } def main(args: Array[String]): Unit = { val result = square(10) println(result) println(square2(100)) println(jieCheng(5)) } } 注意: 方法和函数的输入数据类型 都是val的 并且 不能够显示的使用val 或者var 修饰参数
3.5.3 方法和函数区别
- 区别:
- 函数是一个对象 具有自己的数据类型 和方法 toString hashcode 等方法操作
- 方法:只能够定义在对象或者类的内部,以类的成员存在
- 方法可以升级成函数
-
val 变量= 方法名称 _ //val 变量= 方法名称 _ val fun = square _ println(fun) //function1 注意: 方法提升为对象以后,对象可以赋值给变量,对象可以进行序列化,可以进行保存和网络传输,能够最大限度的增加代码的复用
-
4.Scala中数据结构
- Scala中集合
- Array 数组
- List 链表
- Tuple 元组
- Map 映射
- Set 集
- 在scala中 Array List Map Set 又分为了可变集合和不可变集合操作
- 可变集合 存在于scala.collection.mutable 包
- 不可变的集合 存在于scala.collection.immutabl 包
- 这里的不可变 就相当于javaString类型变量,每次操作都会产生新的对象
- 说明:主要通过增 删 改 查 遍历操作学习集合
4.1 Array
4.1.1 不可变的Array (长度不可变)
-
第一种: val 名称=new Array[数据类型](数组的长度) 第二种: val 名称=Array[数据类型](初始化数据) //val 名称=new Array[数据类型](数组的长度) //创建一个Int类型的长度为3数组 val arr = new Array[Int](3) //val 名称=Array[数据类型](初始化数据) //创建一个String类型的数组 val strArray = Array[String]("hadoop", "scala", "spark") //赋值和修改 strArray(0) = "kafka" for (str <- strArray) println(str)
4.1.2 可变数组 ArrayBuffer (长度和数据都是可以改变)
-
声明格式: val 名称=new ArrayBuffer[数据类型](长度) val 名称=ArrayBuffer(初始化数据) object Scala_day01_arrayBuffer { def main(args: Array[String]): Unit = { //val 名称=ArrayBuffer(初始化数据) val strArrayBuffer = ArrayBuffer[String]("hadoop", "scala", "spark") /** * 增加 * += 可以增加一个 或者多个元素 * 删除 * -= 删除一个或者多个元素 */ strArrayBuffer += "java" strArrayBuffer += ("hbase", "hive", "impala") strArrayBuffer-=("hbase","impala") strArrayBuffer(0)="mapreduce" //遍历 for (str <- strArrayBuffer) println(str) } } 注意:arr(index) 实际上都是方法操作
- 数组:
- 有序 随机遍历效率非常高,删除和随机插入操作效率非常低
- 数组在多线程环境下都是不安全,数组的值可以进行改变
4.2 List
- List是一个链表结构 head(当前存储的元组)和tail(除了当前元素的 所有元素 tail 也是一个list)
4.2.1 不可变list(长度和数据都是不可变)
-
格式: val 名称=List[数据类型](初始化数据) val 名称 = "初始化数据"::"初始化数据":: Nil // val 名称=List[数据类型](初始化数据) val strList = List[String]("hadoop", "scala", "spark") /** * 增加操作 * 头部: :: , +: 向列表头部增加一个数据 * ::: ++: 增加另外一个列表 * 尾部: :+ 向列表头部增加一个数据 * ++ 增加另外一个列表 */ val javaWebList = "javaWeb" :: strList val htmlList = "HTML" +: javaWebList val javaSeList = List("JavaSE", "Mysql", "mybaties") ::: htmlList val elkList = javaSeList :+ "ELK" val flinkList=elkList++List("Flink","kylin","druid") /** * 删除 * drop 方法 */ val dropList=flinkList.drop(6) //从列表的头部开始进行删除 或者列表左边 val dropRightList=dropList.dropRight(3) /** * 修改操作 */ val updateList=dropRightList.updated(0,"java") //遍历 for (str <- updateList) println(str) 注意: 在scala中 凡是以:结尾的方法操作 在使用过程中,都会作用于方法的右侧
4.2.2 可变列表操作 ListBuffer (长度可变)
-
声明格式: val 名称=new ListBuffer[数据类型](初始化长度) val 名称=ListBuffer[数据类型](初始化数据) //val 名称=ListBuffer[数据类型](初始化数据) val strListBuffer = ListBuffer[String]("hadoop", "spark", "scala") /** * 增加操作 * += 添加一个或者多个 * ++= 添加另外一个列表 * 删除操作 * -= 删除一个或者多个 * --= 删除另外一个列表 */ strListBuffer += "hive" strListBuffer ++= List("impala", "hbase", "kafka") strListBuffer-="scala" strListBuffer--=List("hbase","kafka") val updateList=strListBuffer.updated(0,"flink") println(strListBuffer(0)) println("--------------------") for (str <- updateList) println(str)
-
定义个方法,该方法具有多个不同数据类型的返回值(List,Array)
-
def getListAndArray(){ val list=new List() val array=new Array() }
-
4.3 Tuple 元组(不可变)
-
元组:使用小括号括起来的不同数据类型的一个集合 val 名称=(不同数据类型的初始化数据) 注意: tuple 都是继承 TupleN的操作 N 是集合中数据的个数 N<=22 //val 名称=(不同数据类型的初始化数据) object Scala_day01_tuple { def getListAndArray(): Tuple2[List[String], Array[Int]]={ val list = List ("hadoop", "scala", "spark") val array = Array (20, 10, 30) (list, array) //Tuple2 } def main(args: Array[String]): Unit = { //val 名称=(不同数据类型的初始化数据) val tuple: Tuple4[String, Int, Double, Boolean] = ("hadoop", 10, 12.5, true) //访问数据 println(tuple._1) println(tuple._2) println(tuple._3) println(tuple._4) println(tuple.getClass) println("-----------------------") val t= getListAndArray() println(t._1) println(t._2.toBuffer) println("-----------------------") val (strlist,intArray)=getListAndArray() println(strlist) println(intArray.toBuffer) } } val tuple2Array=Array( ("hadoop",10), ("scala",20), ("spark",30) ) //这种结构类似于map 在scala中map中元素是不是基于tuple2构成的呢? 是的
4.4 Map 映射
4.4.1不可变的map操作
-
val 名称=Map[key数据类型,value数据类型](key -> value,key -> value,key -> value) val 名称=Map[key数据类型,value数据类型]((key,value),(key,value),(key,value)) object scala_day01_map { def main(args: Array[String]): Unit = { //val 名称=Map[key数据类型,value数据类型](key -> value,key -> value,key -> value) val map = Map[String, Int]("hadoop" -> 10, "scala" -> 5, "spark" -> 20) //访问map中的元素 println(map("hadoop")) //println(map("java")) println(map.get("hadoop").get) //get方法的返回值 是Option 表示可选向 有值 返回Some(value) 没值 None println(map.getOrElse("java", 100)) //推荐使用这种方法 //+ 增加 和修改 val newMap = map + ("hadoop" -> 100, "fink" -> 30) //删除 val scalaMap = newMap - ("scala", "fink") println(scalaMap) for (t <- map) { println(t._1 + "\t" + t._2) } println("---------------------") for ((k, v) <- map) { println(k + "\t" + v) } } }
4.4.2 可变的Map
-
导包: import scala.collection.mutable val 名称=mutable.Map[key数据类型,value数据类型]() object scala_day01_mutablemap { def main(args: Array[String]): Unit = { //val 名称=new mutable.Map[key数据类型,value数据类型]() val map = mutable.Map[String, Int]() /** * 增加数据 * += 增加一个或者多个元素 或者修改一个或者多个元素 * ++= 增加另外一个map * 删除数据 * -= 删除一个或者多个数据 * 修改 */ map += ("hadoop" -> 10, "spark" -> 20) map ++= Map("scala" -> 100) //不可变的Map map-=("scala","spark") map += ("hadoop" -> 100, "spark" -> 20) for ((k, v) <- map) println(k + "\t" + v) } }
4.5 Set集合
- Set无序的、不可重复的集合
4.5.1 不可变Set集合
-
val 名称 =Set[String](初始化数据) //val 名称 =Set[数据类型](初始化数据) val set = Set[Int](6, 5, 1, 2, 3, 4, 2, 4) val newSet = set + 10 val newSet2 = newSet + (12, 11) val removeSet = newSet2 - (1, 2) //遍历 for (s <- removeSet) println(s) println("----------------------------") val set2 = Set[Int](2, 4, 6, 8, 10) //求并集操作 set set2 val set3 = set ++ set2 println(set3) //求交集 set set2 (2,4,6) val set4=set & set2 println(set4) //求差集 set 与set2 (1,5,3) val set5 =set &~ set2 println(set5)
4.5.2 可变Set
-
先导包 : import scala.collection.mutable val set=mutable.Set[数据类型](初始化数据) //val set=mutable.Set[数据类型](初始化数据) val set = mutable.Set[String]() set += ("hadoop", "scala") set -=("hadoop") for (s <- set) println(s)
5.符号操作
- 不可变的集合: + - ++ :: +: ::: ++: 等
- 可变集合:+= ++= -= --=
6.集合中常用方法操作
- 以Array数组为例 讲解集合中常用的方法操作
6.1 单个集合的操作
-
val arr = Array[Int](1, 4, 6, 3, 8, 9, 1, 4) //求sum值 println(arr.sum) //求最大值最小值 println(arr.max) println(arr.min) //排序 默认升序 println(arr.sorted.toBuffer) //reverse 将集合元素进行翻转 println(arr.sorted.reverse.toBuffer) //去重操作 println(arr.distinct.toBuffer) //通过数组元素构建字符串 println(arr.mkString("-"))
6.2 两个集合的操作方法
-
val intArray = Array[Int](1, 3, 5, 7, 10, 20, 30) val strArray = Array[String]("hadoop", "scala", "spark", "java", "hive") //zip 拉链操作 val zipArray = strArray.zip(intArray) println(zipArray.toBuffer) //zipAll /** * 第一个参数:集合 * 第二个参数:第一个集合中默认数据 * 第三个参数:第二个集合中默认数据 */ val zipAllArray = strArray.zipAll(intArray, "itcast", 100) println(zipAllArray.toBuffer) //创建二维数组 扁平化操作 val arrArray = Array( Array[Int](1, 2, 3), Array[Int](4, 5, 6), Array[Int](7, 8, 9) ) println(arrArray.flatten.toBuffer)
6.3 集合中高阶操作
- 高阶操作:就是将函数作为方法的参数
-
package cn.itcast.day01 object scala_day01_api3 { def main(args: Array[String]): Unit = { val arr = Array[Int](1, 4, 6, 8, 9) //foreach 操作 val f: Int => Unit = (x: Int) => println(x) arr.foreach(f) println("-------------------------") //使用匿名函数 arr.foreach((x: Int) => println(x)) println("-------------------------") //使用匿名函数 输入数据的数据类型可以省略的 arr.foreach(x => println(x)) //将一个集合通过函数作用在集合中的每一个元素 并且构建新的集合 val mapArray = arr.map(x => { x * x }) println(mapArray.toBuffer) //过滤操作 filter 偶数 val filterArray = arr.filter(x => x % 2 == 0) println(filterArray.toBuffer) //count 统计数据个数 val count = arr.count(x => x % 2 == 0) println(count) val strArray = Array[String]( "hadoop,scala,spark,html", "java,javaEE,html,hadoop,java" ) //将数组中的每个元素 按照一定规则进行切分,汇总成一个集合 val flatMapArray: Array[String] = strArray.flatMap(x => x.split(",")) println(flatMapArray.toBuffer) val groupMap: Map[String, Array[String]] = flatMapArray.groupBy(x => x) println("--------------------------") groupMap.foreach((t: Tuple2[String, Array[String]]) => { println(t._1 + "\t" + t._2.toBuffer) }) println("----------------------") val map = Map[String, Array[Tuple2[String, Int]]]( "hadoop" -> Array( ("hadoop", 10), ("hadoop", 20), ("hadoop", 30) ), "spark" -> Array( ("spark", 10), ("spark", 30) ), "java" -> Array( ("java", 100), ("java", 150), ("java", 180) ) ) //需求: 求每一个key值下边 对应值的总和 //hadoop -> 60 spark->40 java ->430 val resultMap: Map[String, Int] = map.mapValues((arr: Array[Tuple2[String, Int]]) => { val valueArray: Array[Int] = arr.map(t => t._2) valueArray.sum }) println(resultMap) println("--------------------------------") val arr2 = Array[Int](1, 4, 6, 8, 9) val sum=arr2.reduce( //x 临时变量 初始值 0 每次操作都会进行累加 //y arr2中每一个元素 (x,y)=>x+y ) println(sum) } }
7.案例 基于scala求wordcount
- 需求:读取三个文件 a.txt b.txt c.txt 统计这三个文件中每个单词出现的次数
- 操作步骤:
- 读取文件 Source.from
- 对每一个文件进行统计操作
- 将每一个文件的统计结果汇总 ListBuffer
- 根据汇总结果 再次进行统计
-
package cn.itcast.day01 import java.io.File import scala.collection.mutable.ListBuffer import scala.io.Source object scala_day01_wordocunt { /** * 统计文件中每一个单词出现的个数 * * @param path * @return */ def getFileData(path: String): Map[String, Int] = { val array = Source.fromFile(new File(path)).getLines().toArray val wordArray: Array[String] = array.flatMap((x: String) => x.split(",")) val wordPair: Array[Tuple2[String, Int]] = wordArray.map((word: String) => (word, 1)) val wordPairArray: Map[String, Array[Tuple2[String, Int]]] = wordPair.groupBy((t: Tuple2[String, Int]) => t._1) val mapResult: Map[String, Int] = wordPairArray.mapValues((f: Array[Tuple2[String, Int]]) => f.length) mapResult } def main(args: Array[String]): Unit = { val array = Array[String]("d:/a.txt", "d:/b.txt", "d:/c.txt") val listBuffer = new ListBuffer[Map[String, Int]]() //统计三个文件中数据 并且汇总到集合中 for (path <- array) { val fileData: Map[String, Int] = getFileData(path) listBuffer += fileData } val flattenList: ListBuffer[Tuple2[String, Int]] = listBuffer.flatten val wordPairListBuffer: Map[String, ListBuffer[Tuple2[String, Int]]] = flattenList.groupBy(t => t._1) val resultData: Map[String, Int] = wordPairListBuffer.mapValues((tuple2ListBuffer: ListBuffer[Tuple2[String, Int]]) => { val mapListBuffer: ListBuffer[Int] = tuple2ListBuffer.map(t => t._2) mapListBuffer.sum }) println(resultData) } }