Scala 编程 -Scala基础
1.课程目标
- 安装Scala编译和运行环境
- 熟悉Scala基本语法以及函数式编程
- 熟练掌握Scala数据结构使用以及集合方法操作
2.Scala基础
2.1Scala概述
- 概念:Scala是既可以面向对象也可以面向函数的编程语言,多范式的编程语言。Scala是基于JVM的编程语言。Scala能够兼容Java的语法
- 面向对象:Scala是纯面向对象的语言。比如1+2: 1 是一个对象 + 方法 2是对象 方法的参数。在scala的世界中,每一个字符或者每一个值都是对象,每一个操作符方法操作
- 面向函数:有两个基本点
- 函数是一个对象。函数和字符或者数值具有同等地位。函数可以赋值给变量、可以作为方法的参数、可以作为方法的返回值。
- 数学f(x)=>y :函数是输入数据到输出数据的映射。比如f(x)=>x*x 。x在这个函数处理过程中不能够改变。不可变变量 是安全。也是函数式编程的基石。
2.2 Scala语言特点
- 优雅:Scala编程方式非常贴合程序员的思维。能够通过简单的代码实现复杂的逻辑。并且易读性非常强
- 表达能力强:Scala中遍历集合一般只需要1-2代码就可以。可以节省50%以上的开发量
- Scala是Spark、kafka等大数据框架的开发语言,Scala能够融入大数据生态圈
- Scala是基于JVM的语言,可移植性
3.Scala编译环境和开发环境的安装
-
官网:https://www.scala-lang.org/
-
安装步骤
- 下载(scala-2.11.8) https://www.scala-lang.org/download/all.html
- mis (能够自动配置环境变量) zip(手动配置环境变量 C:\soft\scala-2.11.8\bin)
- 验证:scala -version
- helloword
- //HelloWord 相当于java中类名
//object 修饰对象
object HelloWord{
def main(args:Array[String]){
println(“helloword”)
}
}
- //HelloWord 相当于java中类名
-
基于IDEA Scala插件安装
-
插件网址:http://plugins.jetbrains.com/plugin/1347-scala
-
注意:安装scala插件最好与IDEA的版本一致
-
安装步骤:
- config–》plugins(file–》setting–》plugins)–》from disk
-
创建Scalaproject
- createproject -》scala–》IDE
-
/** object : 修饰的是一个对象 跟java中class类似 HelloWord:名称 def:用来定义方法的关键词 args: Array[String]:输入参数 Unit:返回值类型 **/ object HelloWord { def main(args: Array[String]): Unit = { println("helloWorld") } }
-
4.Scala基础语法
4.1Scala变量
-
声明格式
-
val/var 变量名称:数据类型=值 val :修饰的是不可变变量 相当于java中final修饰的变量 (scala中推荐使用这种变量) var : 修饰的是可变变量 //val/var 变量名称:数据类型=值 val name:String="itcast" val name2="itcast" //name="itcast2" var age:Int=20 var age2=30 age=30 注意:变量的数据类型可以省略。变量的数据类型由值的数据类型推导而来。scala中变量是强类型
-
4.2 变量的数据类型
-
Scala中数据类型跟Java中数据类型一致
-
整型:Int Byte Shot Long Char
-
浮点:Float Double
-
布尔:Boolean
-
字符串:String
-
scala中以上的数据类型全是对象 相当于java中包装类型
-
val age:Int=20 val name:String="itcast" //字符串操作技巧 //插值器 val nameAge=s"itcast$age" val nameAge2=name+age println(nameAge) //原生态字符串 val str= """sfdsdfdsfdsf sdfsdfd fsfddsf qw qw """ println(str) val str2= """ |sfdsfd |sfsfdsfdsf |sfdsfsdfdss """.stripMargin println(str2)
-
4.3 Scala中操作符
-
算数运算符 + - * / % 等
-
比较运算符 > < >= <= ==
-
逻辑运算符 && || !
-
位运算 >> <<
-
以上运算符跟java中运算符功能一致。以上所有的符号都是方法
-
val add=1.+(2) val add2=1+2 println(add)
5.Scala中的表达式
5.1 If表达式
-
语法结构
-
val 变量名称=if(布尔表达式){ 业务逻辑 }else if(布尔表达式){ }else{ } if表达式是具有返回值的 val score=90 val isOk=if(score>=90) "合格" else "不合格" val isVar=if(score>0) score else "ERROR" val myFeel=if(score>95) "很高兴" println(isOk) println(isVar) println(myFeel) //1.if可以返回不同的数据类型,if返回值变量的类型跟返回值数据类型有关 //2.如果if没有返回值,缺省的返回“()”
-
5.2 块表达式
-
概念:使用花括号括起来的表达式并且具有返回值 格式:val 变量名称={表达式} val h = 20 val w = 30 val area = { h * w } //周长 val girth={ 2*(h+w) } println(area) println(girth) 应用场景: 1.方法内部,代码内聚 2.用作初始化
5.3 for循环
-
for(变量名称 <- 集合/数组/等){ 循环体 } val list = new util.ArrayList[String]() list.add("熟悉Hadoop、spark等分布式数据处理工具") list.add("熟悉Hadoop,spark,storm等计算平台") list.add("熟悉Hadoop、hive、Spark、flink、presto等") list.add("熟练掌握Linux Shell、Flume、Kafka、Redis等相关技术") /** * for(变量名称 <- 集合/数组/等){ * 循环体 * } * 通过角标访问的形式遍历每个元素 */ for(index <- 0 to list.size()-1){ println(list.get(index)) } for(index<-0 until list.size()){ println(list.get(index)) } // to 是闭区间 包含 0 to 5 //until 是开区间 不包含 0 to 5
-
for(变量名称 <- 集合/数组 if 条件表达式* ){ 循环体 } val list = new util.ArrayList[String]() list.add("熟悉Hadoop、spark等分布式数据处理工具") list.add("熟悉Hadoop,spark,storm等计算平台") list.add("熟悉Hadoop、hive、Spark、flink、presto等") list.add("熟练掌握Linux Shell、Flume、Kafka、Redis等相关技术") //遍历数据并且过滤出只包含hadoop单词的数据 并且包含storm /** * 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(变量名称 <- 集合/数组 if 表达式;变量名称 <- 集合/数组 if 表达式){ 循环体 } //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 * 需求:遍历出所有叶子节点 * * for(变量名称 <- 集合/数组 if 表达式;变量名称 <- 集合/数组 if 表达式){ * 循环体 * } * 步骤: * 1. 拿到map key * 2. 循环遍历key值 拿到key对应的list */ val keys=map.keySet().toArray for(key<-keys;index<-0 until map.get(key).size()){ println(map.get(key).get(index)) } 问题:key 是val 不变变得。for每次循环的时候都会重新创建key 注意:凡是省略val或者var的地方,默认的都是val变量
-
for循环表达式 可以具有返回值。集合中的每一个元素都会进行业务逻辑处理,最终返回新的集合 val 集合名称=for(变量<-集合/数组/表达式) yield 业务逻辑 val goods = new util.ArrayList[Int]() goods.add(100) goods.add(50) goods.add(25) //val 集合名称=for(变量<-集合/数组/表达式) yield 业务逻辑 //实现95折 val newGoods = for (index <- 0 until goods.size()) yield goods.get(index) * 0.95 for (g <- newGoods) { println(g) }
6.Scala中函数
-
函数两个基本点:
-
函数是一个对象 val 变量名称=函数
-
函数是一个映射关系 (输入数据列表)=>输出数据 x=>y
-
格式:
-
val 变量名称=(输入数据列表)=>{输出数据} //val 变量名称=(输入数据列表)=>输出数据 //使用:val 返回值名称=变量名称(参数列表) //求平方函数 val square=(x:Int)=>x*x val result=square(5) println(result) 注意:函数的输入参数都是默认都是val修饰的 //val 变量名称=(输入数据列表)=>输出数据 //使用:val 返回值名称=变量名称(参数列表) //求平方函数 val square=(x:Int)=>x*x val result=square(5) println(result) println(square.toString()) //function1 //求面积的函数 val area=(h:Int,w:Int)=>h*w println(area(10,20)) println(area.toString()) //function2 //求立方 val cube:Function3[Int,Int,Int,Int]=(h:Int,w:Int,len:Int)=>h*w*len println(cube(1,2,3)) 等同于 val cube:(Int,Int,Int)=>Int =(h:Int,w:Int,len:Int)=>h*w*len println(cube(1,2,3)) 注意:函数都是继承自FunctionN 其中N代表就是参数的个数 N的最大值22。 //val 变量名称=(输入数据列表)=>输出数据 //使用:val 返回值名称=变量名称(参数列表) //求平方函数 val square: Int=>Int =(x:Int)=>x*x val result=square(5) println(result) println(square.toString()) //function1 //求面积的函数 val area:(Int,Int)=>Int =(h:Int,w:Int)=>h*w println(area(10,20)) println(area.toString()) //function2 //求立方 val cube:Function3[Int,Int,Int,Int]=(h,w,len)=>h*w*len println(cube(1,2,3))
-
-
7.Scala中方法定义
-
格式:def 方法名称(方法参数):返回值数据类型={ 方法体 } /** * 格式:def 方法名称(方法参数):返回值数据类型={ * 方法体 * } * 1. 如果方法体非常简单,可以省略{} * 2. 方法的最后一个表达式就算方法的返回值 * 3. 方法的返回值类型是可以省略的 * 注意:如果方法内部调用其自身则返回值类型不能省略(递归调用) * @param args */ def add(a: Int, b: Int) = a + b //阶乘 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=add(1,2) println(result) println(jiecheng(5)) //120 }
8.方法和函数的区别
-
函数是一个对象,拥有方法
-
方法只能作为对象的成员存在
-
方法转换成函数(提升方法级别)
-
格式: val 变量名称= 方法名称 _ //将方法通过 _ 转换成函数 //将sum转换成function //格式: val 变量名称= 方法名称 _ val sumFun=sum _ println(sumFun.toString()) //function3 println(sumFun(1,2,3))
-
9.Scala中数据结构
- Scala学习5中数据结构
- Array(数组)
- List(列表)
- Tuple(元组)
- Map (映射)
- Set(集)
- 以上数据结构Array、List、Map 、Set又可变和不可变之分
- 说明
- 不可变与java中String类型数据不可变是一致
- 可变一般指的长度可变
- 可变集合放在scala.collection.mutable包中
- 不可变的集合放在scala.collection.immutable包中
- 说明
- 以上数据结构 主要从增、删、改、查、遍历五个操作说明
9.1 不可变Array
声明格式:
//根据数组的长度创建数组
val 名称=new Array[数据类型](长度)
//根据初始化数据创建数组
val 名称=Array[数据类型](初始化数据,以逗号隔开)
注意:不可变数组一旦声明 长度不可变,数据可以改变
// val 名称=new Array[数据类型](长度)
val strArr=new Array[String](3)
//给数组数据赋值
strArr(0)="hadoop"
strArr(1)="spark"
strArr(2)="scala"
strArr(2)="storm" //重新赋值修改数据
//遍历数组strArr1
for(str<-strArr){
println(str)
}
println("-----------------")
// hadoop,spark,scala
// val 名称=Array[数据类型](初始化数据,以逗号隔开)
val strArr2=Array[String]("hadoop","spark","scala")
//遍历数组strArr2
for(str<-strArr2){
println(str)
}
注意:strArr(2):实际上是方法调用 apply方法
9.2 可变数组ArrayBuffer (长度和数据都可变)
-
声明格式: //根据数组的长度创建数组 val 名称=new ArrayBuffer [数据类型](长度) //根据初始化数据创建数组 val 名称=ArrayBuffer[数据类型](初始化数据,以逗号隔开) /** * //根据数组的长度创建数组 * val 名称=new ArrayBuffer [数据类型](长度) * //根据初始化数据创建数组 * val 名称=ArrayBuffer[数据类型](初始化数据,以逗号隔开) */ val strArrBuffer=new ArrayBuffer[String] //hadoop scala spark val strArrBuffer2=ArrayBuffer[String]("hadoop","scala","spark") /** * 增加操作 * += 添加一个或者多个 * ++= 添加一个Array */ strArrBuffer2+="mapreduce" strArrBuffer2+=("html","css") strArrBuffer2++=Array("java","javaEE") /** * 删除操作 * -= 删除一个或者多个数据 * --= 删除一个Array */ strArrBuffer2-="java" strArrBuffer2-=("html","mapreduce") strArrBuffer2--=Array("hadoop","scala") /** * 修改 */ strArrBuffer2(0)="storm" //遍历 for(str<-strArrBuffer2){ println(str) }
-
问题:数组的优点查询速度非常快,存储线性存储。缺点:增删相对较慢。多线程情况下数组不安全的。
9.3 list 列表
-
list:以链表形式存在的集合
-
不可变list
-
数据和长度都是不可改变,相当java String,对list的任何操作都会产生新的list
-
格式: val 名称=List[数据类型](初始化数据,以逗号隔开) 补充:val 名称=item1::item2::tiem3::Nil //val 名称=List[数据类型](初始化数据,以逗号隔开) //hadoop scala spark val hadoopList = List[String]("hadoop", "scala", "spark") /** * 增加 * 头部增加一条数据: “ :: ” " +: " * 尾部添加一条数据: “ :+ ” * 增加list : * 头部: “ ::: ” " ++:" * 尾部添加: " ++ " */ val stormList = "storm" :: hadoopList val kafkaList = "kafka" +: stormList val flinkList = kafkaList :+ "flink" val solorList=List("solor","flume"):::flinkList val redisList=List("redis")++:solorList val aiList=redisList++List("AI") //删除 val dropList=aiList.drop(2) val dorpList2=dropList.dropRight(2) // 修改数据 val zkList=dorpList2.updated(0,"zookeeper") //访问数据 println(zkList(2)) //遍历 for (item <- zkList) { println(item) }
-
-
ListBuffer 可变list :长度可变
-
格式: val 名称=new ListBuffer[数据类型](长度) val 名称=ListBuffer[数据类型](初始化数据) // val 名称=ListBuffer[数据类型](初始化数据) val listBuffer=ListBuffer[String]("hadoop","scala","spark") //增加 listBuffer+="java" listBuffer+=("javaEE","Flume") listBuffer++=List("redis","kafka") //删除操作 listBuffer-="Flume" listBuffer-=("hadoop","scala") //修改操作 val list=listBuffer.updated(0,"flink") //查询 println(listBuffer(0)) //遍历 for(item <- list){ println(item) }
-
问题:空列表
- val 名称=item1::item2::tiem3::Nil
-
问题2: “storm” :: hadoopList 说明 :: 实际上list的方法,但是看起来像字符串的方法
- 凡是以“:”结尾的方法,其作用于“:”右侧
-
问题3:List[Int],Array[Int] 这两种集合都能装Int类型的数据,需求,如果同时需要装多种数据类型的数据?
-
9.4 Tuple(元组)
-
概念:tuple就是使用()括起来的不同数据类型的集合 格式:val 名称=(不同数据类型的数据) 注意:tuple是不可变的 (长度和数据都不可变) //tuple应用场景 // 接受具有多个返回值方法的返回值 def getArrayList():Tuple2[Array[String],List[Int]]={ val arr=Array("hadoop","spark") val list=List(30,60) (arr,list) } def main(args: Array[String]): Unit = { //val 名称=(不同数据类型的数据) val t=("hadoop",100,99.9,true) //访问 通过角标 从1开始 println(t._1) println(t._2) println(t._3) println(t._4) println(t.getClass) //Tuple4 println("---------") val (arr,list)=getArrayList() for (a<-arr){ println(a) } println("---------") for(l<-list){ println(l) } } 说明:Tuple类型:都是TupleN N<=22
9.5 Map(映射)
-
不可变Map
-
格式:val 名称=Map[key数据类型,value数据类型](key->value *) 格式:val 名称=Map[key数据类型,value数据类型]((key,value) *) val tuple2="name"->"dddf" //("name","dddf") //格式:val 名称=Map[key数据类型,value数据类型](key->value *) val personMap=Map("name"->"itcast","age"->10,"sex"->"man") val personMap2=Map( ("name","itcast") ) //查询map中元素 println(personMap.get("name").get) // Some 有值 None 没有 println(personMap.get("salary")) println(personMap.getOrElse("salary","20K")) println("---------------------------") //增加 // + val salaryMap=personMap+("salary"->"10K","dept"->"Java") println(salaryMap) println("---------------------------") //删除 - val ageMap=salaryMap-("age","sex") println("ageMap:"+ageMap) println("---------------------------") val newSalaryMap=ageMap+("salary"->"20K") println("newSalaryMap:"+newSalaryMap) println("---------------------------") //遍历map for((k,v)<-personMap2){ println(k+"\t"+v) } //第二种遍历方式 for(t<-personMap){ println(t._1+"\t"+t._2) }
-
-
可变Map
-
导包 scala.collection.mutable
-
格式:val 名称=mutable.Map[key数据类型,value数据类型](key->value *) 格式:val 名称=mutable.Map[key数据类型,value数据类型]((key,value) *) //val 名称=mutable.Map[key数据类型,value数据类型](key->value *) val map=scala.collection.mutable.Map("hadoop"->30,"scala"->20,"spark"->50) //增加 += map+=("java"->10,"hadoop"->15) //删除 -= map-=("spark","scala") //遍历操作 for((k,v)<- map) println(k+"\t"+v)
-
9.6 Set(集)
-
Set特点:
- 无序 (输出顺序与输入顺序无关) 唯一(不能存在重复元素)
-
不可变Set
-
格式:val 名称=Set[数据类型](初始化数据) //val 名称=Set[数据类型](初始化数据) val set=Set[Int](1,2,2,4,10,6,8,9) //增加 + val newSet=set+(11,12) //删除 - val newSet2=newSet-(1,2) //遍历 for(s<-newSet2) println(s) val set2=Set[Int](3,4,6,5,7) println("----------------") //两个set的并集 ++ val bingSet=set++set2 println(bingSet) println("----------------") //交集 “&” val jiaoSet=set & set2 println(jiaoSet) //4,6 //差集操作 &~ val chaSet=set &~ set2 println(chaSet) //1,2,10,8,9
-
-
可变Set
-
导包 scala.collection.mutable
-
格式:val 名称=mutable.Set[数据类型](初始化数据) //val 名称=mutable.Set[数据类型](初始化数据) val set = mutable.Set("hadoop", "scala", "spark") //增加 += //删除 -= set += "java" set += ("javaee", "flume") set -= "scala" set -= ("spark", "hadoop") //更新 set.update("kafka",true) set.update("redis",false) //遍历 for (s <- set) { println(s) }
-
9.7 符号操作
- 对于不可变的集合:+ - ++ +: :: :+ ::: ++: :++
- 对于可变集合:+= ++= -= --=
- 连个set的操作符号: ++(并集) &~(差集) &(交集)
10.集合API操作
-
以Array数组为例操作API
-
单个Array的操作
-
val arr=Array(1,3,5,10,6,8,3,3) //求和 println(arr.sum) //最大值 println(arr.max) //最小值 println(arr.min) //去重复操作 println(arr.distinct.toBuffer) //排序 默认升序 println(arr.sorted.toBuffer) //倒序操作 println(arr.sorted.reverse.toBuffer)
-
-
两个Array的操作
-
val arr=Array(1,3,5,10,6,8,3,3) val strArr=Array("hadoop","spark","scala","flume") //拉链操作 zip val zipArray=strArr.zip(arr) println(zipArray.toBuffer) println("--------------------------------") //zipAll val zipAllArray=strArr.zipAll(arr,"itcast",100) println(zipAllArray.toBuffer) //扁平化 val arrArray=List( Map("name"->"zs"), Map("age"->18), Map("score"->95.5) ) //把二维数组压扁成一维数组 println(arrArray.flatten.toBuffer)
-
-
Array高阶操作
-
//高级操作:把函数当做方法参数 val printFun = (str: String) => println(str) //111foreach arr.foreach(printFun) println("----------------") arr.foreach((str: String) => println(str)) println("----------------") ///22222map // map操作 将集合中每个元素都进行函数操作 转换成型的集合 val mapArray = arr.map((x: String) => x + "_itcast") println(mapArray.toBuffer) println("----------------") ///333333filter //过滤操作 过滤出包含h字符的元素 val filterArray = arr.filter((x: String) => x.contains("h")) println(filterArray.toBuffer) println("----------------") ///444444444444flatMap //将数组中的数据进行分割 按照空格分割 合并成一个数组 val array2 = Array[String]("hadoop mapreduce hdfs", "hive hadoop mapreduce") val flatMapArray = array2.flatMap((x: String) => x.split(" ")) println(flatMapArray.toBuffer) ///555555555555groupBy //groupBy操作 分组操作 val groupByMap = flatMapArray.groupBy((x: String) => x) println(groupByMap) println("----------------") val intArray = Array(1, 3, 5, 7, 8) ///666666666666reduce //x:初始化变量 0 相当于 临时变量 sum //y: 是数组中每个元素 val result: Int = intArray.reduce((x: Int, y: Int) => x+y) println(result) println("----------------") //map集合中高阶操作 val map = Map[String, Array[Tuple2[String, Int]]]( "hadoop" -> Array( ("hadoop", 10), 10 ("hadoop", 20), 20 ("hadoop", 30) 30 ), "spark" -> Array( ("spark", 10), ("spark", 30) ), "java" -> Array( ("java", 100), ("java", 150), ("java", 180) ) ) 777777777777 /** * 需求:hadoop->60 spark ->40 java ->430 */ val resultMap= map.mapValues((x:Array[Tuple2[String, Int]])=>{ val intArray=x.map((t:Tuple2[String, Int])=>t._2) intArray.sum }) println(resultMap)
-
11.WordCount案例
-
需求:准备三个文件 a.txt b.txt c.txt, 统计三个文件中所有单词出现的次数
-
操作步骤:
- 读取文件 Source.fromFile
- 分别统计每个文件中的数据
- 将以上三个文件中数据进行合并
- 将合并的数据再次进行统计
-
代码
-
/** * - 读取文件 Source.fromFile * - 分别统计每个文件中的数据 * - 将以上三个文件中数据进行合并 * - 将合并的数据再次进行统计 */ object Scala_22_wordcount { def getResultByFile(filePath: String): Map[String, Int] = { val str = Source.fromFile(new File(filePath)).getLines().mkString(",") val map = str.split(",").map((x: String) => (x, 1)).groupBy((t: Tuple2[String, Int]) => t._1) val result = map.mapValues((arr: Array[(String, Int)]) => { arr.length }) result } def main(args: Array[String]): Unit = { //读取文件 Source.fromFile val fileArray = Array("d:/a.txt", "d:/b.txt", "d:/c.txt") //聚合每一个文件的统计数据 val list = ListBuffer[Map[String, Int]]() for (filePath <- fileArray) { val resultMap = getResultByFile(filePath) list += resultMap } val groupMap = list.flatten.groupBy((t: Tuple2[String, Int]) => t._1) val result= groupMap.mapValues((arr: ListBuffer[Tuple2[String, Int]]) => { arr.map((t: Tuple2[String, Int]) => t._2).sum } ) println(result) } }
-
api :https://www.scala-lang.org/api/current/?_ga=1.178639076.1310790544.1468501313
-