集合分类Scala集合分为两大类可变(mutable)和不可变(immutable)不可变:数组声明好,数组的长度就不能再改变可变:数组声明好,可就改变数组的长度三大集合Seq集合有序可重复代表List()不可变的List()默认无需导入创建vallist:List[Int]=List(1,2,3,4)修改内容list.updated(1,11)//通过索引修改查,遍历//查,取值list(索引)//遍历list.foreach(println)for(l<-list){println(l)}可变ListBuffer()需要导入创建集合varlistBuffer:ListBuffer[Int]=ListBuffer(1,2,3,4)增加元素listBuffer.insert(1,11)//索引插入listBuffer.append(1)//加在最后删除元素listBuffer.remove(1)//通过索引删值listBuffer.remove(1,2)//索引,几个listBuffer.drop(2)//从索引0开始删几个修改元素listBuffer.update(0,11)//通过索引修改元素查询,遍历元素//查询,取值listBuffer(索引)//遍历listBuffer.foreach(println)for(l<-listBuffer){println(l)}特殊的List()集合空集合println(List())//List()println(Nil)//List()用处1::2::NilSet集合无序不可重复使用Set集合需要记住,无序(就表示需要索引的操作不能用)主要使用是生成新的集合,本身变化不大创建Set集合//默认是不可变的valset:Set[Int]=Set(1,2,1,2,3,4)//可变Set集合valmSet:mutable.Set[Int]=mutable.Set(1,2,3,44,5,5,5,1,2)一些操作//添加删除varmSet1:mutable.Set[Int]=mSet+11println(mSet1.mkString(","))varmSet2:mutable.Set[Int]=mSet-1println(mSet2.mkString(","))---------------------------------------1,2,3,5,11,442,3,5,44Map集合无序,(k->v),key不能重复,value可重复。创建//创建不可变valmap:Map[String,Int]=Map("a"->1,"b"->2)//创建可变valmMap:mutable.Map[String,Int]=mutable.Map("aa"->1,"bb"->2,"cc"->3)增加valmap1:Map[String,Int]=map+("c"->3)println(map1.mkString(","))-------------------------------------------a->1,b->2,c->3修改valmap2:Map[String,Any]=map.updated("a","11")println(map2.mkString(","))//可变Map可以这样改mMap("aa")=11println(mMap.mkString(","))//还可以通过添加覆盖的思想修改valmap4:Map[String,Any]=map+("a"->"22")println(map4.mkString(","))---------------------------------------------------a->11,b->2aa->11,bb->2,cc->3a->22,b->2健取值vali:Int=map("a")println(i)---------------------1删除valmap3:Map[String,Int]=map-"a"println(map3.mkString(","))--------------------------------------b->2
引言Scala数组分为两大类可变(mutable)和不可变(immutable)Array()数组介绍Array为不可变数组不可变是指内存地址不变,一但确定,数组的长度不能变创建valints:Array[Int]=Array(1,2,3,4,5)数组取值注意是()ints(0)修改元素updata(索引,新值)//按照索引修改ints.update(1,10)//updata(索引,新值)添加元素不可变数组添加元素是指添加后产生新的数组,并不是在原数组添加//注意连接的符号valints1=ints:+12println(ints1.mkString(","))----------------------------1,2,3,4,5,12valints2=15+:intsprintln(ints2.mkString(","))----------------------------12,1,2,3,4,5遍历数组使用for循环遍历for(elem<-ints){println(elem)}使用方法foreach(函数)函数返回值要是Unit函数接收的参数就是数组的元素//下面是简化过程ints.foreach((i:Int)=>{println(i)})ints.foreach(println(_))ints.foreach(println)ArrayBuffer()介绍ArrayBuffer()为可变数组创建valarrayBuffer:ArrayBuffer[String]=ArrayBuffer("a","b","c","d")添加元素insert(索引,新元素)插入元素arrayBuffer.insert(1,"B")println(arrayBuffer.mkString(","))----------------------------------a,B,b,c,d+=组合元素+==组合数组arrayBuffer+="e"println(arrayBuffer.mkString(",")---------------------------------a,b,c,d,earrayBuffer++=Array("a","zz")println(arrayBuffer.mkString(",")---------------------------------a,b,c,d,e,a,zz删除元素使用索引删除arrayBuffer.remove(1)//索引删除arrayBuffer.remove(1,2)//从索引开始删几个arrayBuffer-=1删除数组中的指定元素数组中没有找到也不会报错arrayBuffer--=Array("a")修改元素arrayBuffer(0)="A"查看元素//通过索引获取println(arrayBuffer(0))遍历数组//函数arrayBuffer.foreach(println(_))//for循环for(buffer<-arrayBuffer){println(buffer)}数组可变性的转换可变转不可变toArrayvalarray:Array[String]=arrayBuffer.toArray不可变转可变toBuffervalbuffer1:mutable.Buffer[Int]=ints.toBuffer
引言Scala不仅是完全面向函数编程,还是完全面向对象语言。格式类的定义class类名{//类主体//定义类属性//Scala类属性是需要初始化的,初始化只需要_(下划线)就可以了varname:String=_varage:Int=_//类方法defshow(){函数逻辑}}类的使用//创建类对象val对象名:类属性=new类名称//获取类属性对象名.属性名//设置类属性对象名.属性名=值//执行类方法对象名.方法名()如果使用val声明属性,属性将不能被修改,底层会有final修饰私有属性属性被private修饰,将不能在类外边直接访问。小案例定义User类classUser{varname:String=_varage:Int=_defshow():Unit={println(s"name:$name;age:$age")}}类使用objectdemoextendsApp{//创建类对象valuser:User=newUser()//设置类属性user.name="Bob"user.age=16//获取类属性println(user.name)//执行类方法user.show()}----------------------------------Bobname:Bob;age:16
引言会涉及_(下划线)的其中一个用法Scala的宗旨:至简。所以把匿名函数作为函数的参数,可以进行很大程度的简化有一个参数的简化定义一个函数deffun(f:Int=>Unit):Unit={f(12)}执行函数传入匿名函数fun((a:Int)=>{println(a)})匿名函数的参数类型,可以被自动推断出来,可以简化fun((a)=>{println(a)})参数只有一个,代码在一行,代码块括号可以简化fun(a=>println(a))下面是println()函数的特殊简化,println()本身也是一个函数参数后面只用一次,可以用_(下划线)代替fun(println(_))fun(println)---------------12有多个参数的简化定义一个函数(两个参数)deffn(f:(Int,Int)=>Int):Unit={println(f(12,23))}执行函数传入匿名函数fn((a:Int,b:Int)=>{a+b})匿名函数的参数类型,可以被自动推断出来,可以简化fn((a,b)=>{a+b})代码在一行,代码块括号可以进一步简化fn((a,b)=>a+b)如果参数只用了一次可以使用_fn(_+_)-------35
引言Scala不仅是完全面向对象语言,而且还是完全面向函数编程语言,所以Scala叫做多范式编程语言,Scala的函数非常强大函数可以作为函数的返回值函数可以作为函数的参数函数可以赋值给变量函数作为返回值函数作为返回值,需要注意_(下换线)的用法把返回值函数写在外边//定义一个函数,作为返回值deffun(name:String):Unit={println(s"fun_name=$name")}//定义执行函数deffun1(name:String)={//加个_(下划线)表名该函数是返回值println(s"fun1_name=$name")fun_}//执行函数,返回值函数不会被执行fun1("Bob")-------------------------fun1_name=Bob//执行返回值函数fun1("Bob")("bigdataboy")-------------------------fun1_name=Bobfun_name=bigdataboy把返回值函数写在内部函数执行结果与上面一样,只是这样写跟容易理解deffun1(name:String)={//返回值函数deffun(name:String):Unit={println(s"fun_name=$name")}println(s"fun1_name=$name")fun_}函数柯里化就是把上面的格式进行简化,通过函数调用就可以看出来所以Scala里就会看到()()()()()()这样的调用deffun(name:String)(name1:String):Unit={println(s"name=$name")println(s"name1=$name1")}fun("Bob")("Bbigdataboy")----------------------------------------------fun1_name=Bobfun_name=bigdataboy函数作为参数把函数作为参数,不同点在于规定参数类型时的写法不一样deffun(f:String=>Unit):Unit={f()}//定义参数类型解析f:String=>Unit参数名称:传入函数的参数类型=>传入函数的返回值类型小例子自定义传入一段逻辑(函数)给函数deffun(f:Int=>Int,num:Int):Unit={//执行传入的函数(有参数也需要传参数)println(f(num))}参数函数//把传入的参数不做任何处理直接返回deffn(n:Int):Int={n}执行函数fun(fn,5)---------5传入匿名函数(作为参数)这也是Spark用的最多的,匿名函数可以看看这个//定义一个函数deffun(f:()=>Unit):Unit={f()}执行函数(传入匿名函数)fun(()=>{println("Hellobigdataboy")})---------------------------------------Hellobigdataboy函数赋值给变量变量可以接受一个匿名函数valadd=(a:Int,b:Int)=>a+b//调用println(add(12,34))---------------------------------46
如果你有Python基础,那么Scala的函数基础使用,你能快速的上手格式def函数名(参数名:参数类型,参数名:参数类型):返回值类型={方法体}defmani(args:Array[String]):Unit={print("Helloworld")}函数的入参(参数)和出参(返回值)有参数有返回值deffun(a:String):String={returna+"bigdataboy.cn"}println(fun("https://"))有参数没有返回值deffun(a:String):Unit={println(a)}fun("bigdataboy.cn")没有参数有返回值deffun():String={"HelloScala"}println(fun())没有参数没有返回值deffun():Unit={println("HelloScala")}fun()可变参数与Python类似,用*号表示可变参数可以传参,也可以不传参可变参数一般放在参数列表的最后面deffun(name:String*):Unit={println(name)}fun()fun("Bob","Black")--------------------------------List()ArraySeq(Bob,Black)默认参数默认参数与Python类似deffun(name:String,age:Int=13)={println(name,"-",age)}fun("Bob")fun(name="Bob",age=16)---------------------------------------(Bob,-,13)(Bob,-,16)精简函数格式如果能通过最后一行,推测出返回值类型,就可以不写返回值类型deffun()={println("Scala")"Scala"}//调用println(fun())--------------------ScalaScala可以使用分号隔开逻辑写成一行deffun()={println("Scala");"Scala"}//调用println(fun())--------------------ScalaScala在一行就可以去掉大括号。去掉大括号,写在一行就只对最近的一个逻辑有效,所以后面打印的是()deffun()=println("Scala");"Scala"//调用println(fun())--------------------Scala()如果函数,没有参数,可以去掉参数括号,但是在调用的时候,也不用加括号deffun=println("Scala");"Scala"//调用println(fun)-----Scala()小技巧:当明确没有返回值时,函数的=可以省略,编译器也就不会把最后一行代码作为返回值。deffun4(){println("Scala");"Scala"}println(fun4())--------------------------------------Scala()匿名函数格式()->{代码块}例子()->{vara=1varb=2println(a+b)}//写成一行分号区分代码块()->{vara=1;varb=2;println(a+b)}-------------------------------------------3
格式for(i<-0to|until5){循环体}for(i<-1to5){println(s"i=$i")}i:i是接收for循环次数的变量,不用声明是因为Scala能自动推断to:是[0,5],是前后都包含的意思until:是[0,5),是前包含后不包含的意思详解to和until到Range()控制循环步长1.to(5)-简写->1to50.until(5)-简写->0until5until和to的源码里都调用了一个Rang()的方法,所以for循环还能这样写for(i<-Range(0,5)){println(s"第$i次")}----------------------第0次第1次第2次第3次第4次Range()控制循环步长for(i<-Range(0,5,2)){println(s"第$i次")}----------------------第0次第2次第4次小案例一层循环打印九层妖塔for(i<-Range(1,18,2)){println(""*((18-i)/2)+"*"*i+""*((18-i)/2))}守卫循环循环守卫:所谓循环守卫就是加一个判断,成立执行循环体,不成立跳过本次循环跟Java和Python中的continue一样for(i<-1to6ifi%2==0){println(s"i=$i")}-------------------------------i=2i=4i=6其实也可以等价于for(i<-1to6){if(i%2==0){println(s"i=$i")}引入变量格式//原本格式,分号隔开for(i<-1to5;f=i%2){println(s"i=$i:f=$f")}//写一行;隔开逻辑,注意大括号for{i<-1to5;f=i%2}{println(s"i=$i:f=$f")}//不要分号换行隔开逻辑for{i<-1to5f=i%2}{println(s"i=$i:f=$f")}所以小案例变换//要分号for{i<-Range(1,18,2);j=((18-i)/2)}{println(""*j+"*"*i+""*j)}//不要分号代码块for{i<-Range(1,18,2)j=((18-i)/2)}{println(""*j+"*"*i+""*j)}for的返回值一般情况for都没有返回值//for的返回值一般情况for的返回值都是Unitvalres:Unit=for(i<-1to5){"abc"}yeild特殊情况它会把结果返回成一个集合valvalue=for(i<-1to5)yieldiprintln(value)-------------------------------------Vector(1,2,3,4,5)for跳出循环Scala是完全面向对象的语言,所以没有break。Scala使用的产生一个特定的异常,然后进行类似异常处理来保证后面代码的正常运行//产生异常,结束循环for(i<-1to5){if(i==3){Breaks.break()}println(s"s=$i")}----------------------s=1s=2Exceptioninthread"main"scala.util.control.BreakControl对异常进行特定处理,保证后面正常运行Breaks.breakable(//这对小括号可以换成大括号(表示包裹代码块)for(i<-1to5){if(i==3){Breaks.break()}println(s"s=$i")})println("跳出循环")---------------------------s=1s=2跳出循环
单分支结构式varfalg=trueif(falg){println("true")}双分支结构式varfalg=trueif(!falg){println("true")}else{println("false")}多分支结构式varname="a"if(name.equals("a")){println("a")}elseif(name.equals("b")){println("b")}else{println("未知")}小技巧当判断的代码块不写{}时,只对最近的一行代码有效//两种结果写法一样varfalg=trueif(falg)println("true")if(falg){println("true")}var&=trueif(!&)println("true")elseprintln("false")条件判断的返回值在Scala中,任何的表达式都有返回值条件判断的返回值就是每个代码块的最后一行的返回值返回值类型Any,AnyVal返回类型最方便是不写Any是返回值类型是String和Unit等等AnyVal是返回值类型是Int和Unit等等varmsg=truevalreg:Any=if(msg){"abc"//Scala可以这样单独写个值,Java不行}println(reg)定义返回值类型需要能确定全部返回类型varmsg=truevalreg:String=if(msg){"true"}else{"false"}println(reg)Scala中的“三元运算符”Scala中没有标准的三元运算符,也没有必要有,就省略了//Java中的三元运算符Stringnum=if(13==12)?"0":"1"//Scala中模拟的三元运算符varnum=if(13==12)"0"else"1"
groupby分组通常会和聚合函数一起使用,按照某个字段的内容进行分组,然后每个分组执行聚合操作原本数据idnamemoney1Bob12002Black21003BigDataBoy56004Bob23005Bob32006Black5600需求:按照姓名(name)进行分组,求平均工资(money)查询语句:selectname,avg(money)fromhive_dbgroupbyname;查询结果hive>selectname,avg(money)fromhive_db>groupbyname;QueryID=root_20200127203058_a0984c91-9ca1-4735-b6a9-ffd5aa2d17a7...TotalMapReduceCPUTimeSpent:18seconds160msecOKBigDataBoy5600.0Black3850.0Bob2233.3333333333335Timetaken:48.392seconds,Fetched:3row(s)having语句having与where语句的异同where针对表中的列发挥作用,查询数据;having针对查询结果中的列发挥作用,筛选数据。where后面不能写分组函数,而having后面可以使用分组函数。having只用于groupby分组统计语句。需求:不同名字(name)的平均工资(money)大于3000的查询语句:selectname,avg(money)avg_moneyfromhive_dbgroupbynamehavingavg_money>3000;查询结果:hive>selectname,avg(money)avg_moneyfromhive_db>groupbyname>havingavg_money>3000;QueryID=root_20200127204801_68c6a01d-21a3-4355-b3a8-69900f16b857...TotalMapReduceCPUTimeSpent:16seconds10msecOKBigDataBoy5600.0Black3850.0Timetaken:46.82seconds,Fetched:2row(s)
Join语句Hive支持通常的SQLJOIN语句,但是只支持等值连接,不支持非等值连接。表1(hive_db)idnamemoney1Bob12002Black21003BigDataBoy56004Bob23005Bob32006Black5600表2(city)nameaddrBobBeijingBlackHuNanBigDataBoySiChuan查询案例需求:表1的姓名(name)与表2的addr合并idnameaddr查询语句selecth.id,h.name,c.addrfromhive_dbhjoin#连接的另一张表citycon#筛选条件不支持orh.name=c.name;查询结果hive>select>h.id,h.name,c.addr>from>hive_dbh>join>cityc>on>h.name=c.name;QueryID=root_20200127215351_3f0945cf-dfd3-4c00-ba9a-b177cecf018f...MapReduceJobsLaunched:Stage-Stage-3:Map:1CumulativeCPU:2.86secHDFSRead:6494HDFSWrite:91SUCCESSTotalMapReduceCPUTimeSpent:2seconds860msecOK1BobBeijing2BlackHuNan3BigDataBoySiChuan4BobBeijing5BobBeijing6BlackHuNanTimetaken:31.054seconds,Fetched:6row(s)内连接join只有进行连接的两个表中满足连接条件的数据才会被保留下来。selecth.id,h.name,c.addrfromhive_dbhjoincityconh.name=c.name;左外连接leftjionon或者where筛选条件的左边的表的查询字段的所有数据将会保留下来selecth.id,h.name,c.addrfromhive_dbhleftjoincityconh.name=c.name;右外连接rightjionon或者where筛选条件的右边的表的查询字段的所有数据将会保留下来selecth.id,h.name,c.addrfromhive_dbhrightjoincityconh.name=c.name;满外连接fulljionon或者where筛选条件的两边的表的查询字段的所有数据将会保留下来,任一表的指定字段没有符合条件的值用NULL代替selecth.id,h.name,c.addrfromhive_dbhfulljoincityconh.name=c.name;多表连接注意:连接n个表,至少需要n-1个连接条件。例如:连接三个表,至少需要两个连接条件。