這一篇博文是【大數據技術●降龍十八掌】系列文章的其中一篇,點擊查看目錄:大數據技術●降龍十八掌
- 系列文章:
- 【十八掌●武功篇】第十六掌:Spark之Scala安裝和HelloWorld
【十八掌●武功篇】第十六掌:Spark之Scala語法快速概覽
【十八掌●武功篇】第十六掌:Spark之簡介、安裝、Demo
【十八掌●武功篇】第十六掌:Spark之RDD簡介
1、 函數
(1) 一個最典型的函數實例
def max(x: Int, y: Int): Int = {
if (x > y)
x
else
y
}
- def關鍵字是定義一個max
- max是函數名稱
- x是第一個參數名,:Int是指定參數x的類型爲Int,y是第二個參數名稱
- 小括號後面的:Int是表示函數返回值爲Int
- =後面是函數體,=符號可以理解爲將後面的函數體賦值給前面的函數定義。當函數顯示地指定了返回值時,這個=是不可以省略的,如果沒有顯示定義返回值可以省略這個=。
- Scala和JavaScripte裏類似,一行的結束可以不用分號;
- Scala的函數可以沒有return,運行時,執行的最後一條語句的返回值就是函數的返回值。
在Scala命令行中運行函數定義和調用:
scala> def max(x:Int,y:Int):Int={ //定義函數
| if(x>y)
| x
| else
| y
| }
max: (x: Int, y: Int)Int
scala> max(2,3) //調用函數
res0: Int = 3
(2) 無參數,無返回值的函數
def add1() = {
println("add1....")
}
//無參數的函數可以省略 =
def add2() {
println("add2....")
}
//無返回值的函數,返回值其實是Unit類型的,
// Unit可以省略,就像第一個add1一樣
def add3(): Unit = {
println("add3....")
}
調用時,如果函數沒有參數,可以省略()
scala> def add3(): Unit = {
| println("add3....")
| }
add3: ()Unit
scala> add3 //可以省略()
add3....
(3) 函數體就一行代碼時
//當函數體只有一行代碼時,可以將花括號{}省略
//所以當無參數、無返回值、一行代碼的函數,可以寫爲這樣
def add4() = println("add3....")
(4) 匿名函數
//一個函數
def add5(x: Int, y: Int) = x + y
//改爲匿名函數
(x: Int, y: Int) => x + y
匿名函數的函數體前面要用 => 符號。
(5) 將函數賦值爲常量
//將匿名函數賦值給常量
val add6=(x: Int, y: Int) => x + y
//一個無參數函數
def add8() = println("add8")
//一個有參數函數
def add9(x: Int, y: Int) = x + y
//將無參數函數賦值給常量
val add10 = add8;
//將有參數函數賦值爲常量,必須加 _
val add11 = add9 _;
(6) 在函數中調用函數
如果想將上面的add6傳遞給另外一個函數add7,add7應該定義如下:
//將函數賦值給函數
def add7(f: (Int, Int) => Int): Int = {
f(3, 8)
}
上面的(Int,Int)=>Int是定義了一個類型,類型是個匿名函數,參數f的類型就是個函數,這個函數的輸入參數是兩個Int類型的值,返回值也是個Int類型的值,另外add7函數的返回值也是個Int類型。
函數體中f(3,8)是說,函數體是去執行輸入的那個匿名函數。
(7) 默認參數值
//name參數可以不傳遞,不傳遞時使用默認值:xiaoming
def sayName(name: String = "xiaoming") {
println("Hello!" + name)
}
定義函數時可以給參數指定一個默認值,當不傳遞這個函數時函數體就使用指定的默認值。一般慣例是將有默認值的參數參數列表的最後面。
(8) 變長函數
def printCourse2(course: String*): Unit = {
course.foreach(x => println(x))
}
def printCourse(course: String*): Unit = {
var txt = "";
course.foreach(x => (txt += (x + ",")))
println("input:" + txt)
}
在Scala中,定義函數時,最後一個參數可以變長,變長的意思是:參數的個數不確定,可以有很多個,類型後面添加一個*字符表示這個參數是可以變長的,調用時如下:
printCourse2(“a”,”b”,”c”)
2、 循環
(1) 循環表達式
- 1 to 10:循環1到10,包括1和10,包頭包尾。
- 1.to(10) 同1 to 10
- 1 until 10 : 循環1到10,不包含10,包頭不包尾。
- 1.until(10) 同 1 until 10
- Range(1,10) 同 1 to 10
- Range(1,10,2) 是從1到10,步長爲2,就是循環1、3、5、7、9
(2) for 循環
def main(args: Array[String]) {
var sum = 0;
for (i <- 1 to 10) {
sum += i
}
println(sum)
}
參與循環的變量i與i取值範圍之間用<-表示。
1 to 10明確表明了取值範圍是1到10,包括1和10。
sum = 0
for (i <- 1 until 10)
sum += i
println(sum)
until是不包括10的,取值是1到9
for (c <- "Hello")
println(c)
遍歷字符串並不需要下標。
(3) for循環嵌套
for (i <- 1 to 3; j <- 1 to 3)
println(i.toString + j.toString)
(4) for循環中判斷
for (i <- 1 to 3; j <- 1 to 3 ; if i>j)
println(i.toString + j.toString)
(5) break、coutinue
Scala中沒有break和coutinue關鍵字,如果想實現類似功能,可以使用scala.util.control.Breake來是實現。
object BreakDemo {
def main(args: Array[String]) {
//定義一個集合
val numList = List(1, 2, 3, 4, 5, 6, 7, 8)
//實例化一個Breaks對象,
val loop = new Breaks
loop.breakable(
//將循環放入loop.breakable中
for (num <- numList) {
println(num)
if(4==num)
{
//使用loop.break()方法來實現跳出
loop.break()
}
}
)
}
}
3、 數組
(1) 定長數組
- 定長數組定義:val list=Array(1,2,3,4,5) 或者 val list2=new ArrayInt
- 賦值:list(0)=11。下標用小括號來指定,而不像Java中一樣用方括號。
(2) 變長數據
- 變長數組定義:val listBuffle=scala.collection.mutable.ArrayBufferInt
- 添加一個元素:listBuffle+=2 添加一個元素2。
- 添加多個元素:listBuffle++=Array(5,6,7) 添加了三個元素到變長數組裏。
listBuffle+=(8,9,10) 添加了一個元組。 - 插入元素:listBuffle.insert(0,0)在0的位置插入一個值0。
- 刪除元素:listBuffle.remove(4) 刪除4位置的元素。
(3) 變長數據轉爲定長數組
scala> listBuffle.toArray
res17: Array[Int] = Array(0, 1, 2, 3, 5, 6, 7, 8, 9, 10)
(4) 遍歷數組
for(i<-listBuffle) println(i)
(5) 數組變長字符串
listBuffle.mkString(“|”) 將數組變爲字符串,用 | 字符隔開
listBuffle.mkString(“[“,”|”,”]”) 將數組變爲字符串,用|隔開,前面添加[,後面添加]。
4、 元組
元組是可以存儲不同類型元素的集合。
有兩個元素的元組叫二元組。
(1) 定一個元組
scala> val tuple=(1,2,4,"a")
tuple: (Int, Int, Int, String) = (1,2,4,a)
元組在Spark中很常見,比如key、value對可能就是個元組,{1001:”beijing”}
(2) 獲取元素
可以用tuple._1獲取第一個元素,注意元素的下標從1開始。
(3) 遍歷元組
for(i<- 1 to tuple.productArity-1 ) println(tuple.productElement(i))
- tuple.productArity是獲取元組的長度
- tuple.productElement是獲取元組的某一個下標的元素,請注意這裏的下標是從0開始。
5、 集合
(1) Scala集合介紹
- Scala有一個非常豐富、強大可組合的集合庫,集合都在scala.collection包中。
- Scala集合中有定長集合和變長集合,定長集合在scala.collection. immutable中,變長集合在scala.collection. mutable中
(2) Scala集合結構
(3) List集合
● 定義一個List:val list=List(1,2,3,4,5)
● List由head和tail組成:
scala> list.head
res34: Int = 1
scala> list.tail
res35: List[Int] = List(2, 3, 4, 5)
head是集合中第一個元素,tail是除了head外的其他元素組成的集合,也就是tail是個List集合,tail也有head和tail,如下:
scala> list.tail.tail.tail
res36: List[Int] = List(4, 5)
● 空集合用Nil表示,同List()
● 另外一種定義集合方式,是用head和tail方式定義,如下:
//head爲1,tail爲空集合
scala> var list2=1::Nil
list2: List[Int] = List(1)
//head爲2,tail爲一個集合
scala> var list3=2::list2
list3: List[Int] = List(2, 1)
//另一種定義方式
scala> var list4=1::2::3::list3
list4: List[Int] = List(1, 2, 3, 2, 1)
//上面方式可以理解爲這種方式
scala> var list5=(1::(2::(3::list3)))
list5: List[Int] = List(1, 2, 3, 2, 1)
● List轉換爲數組
scala> list.toArray
res3: Array[Int] = Array(1, 2, 3, 5)
● List中添加、減少元素
List是定長集合,所以不能添加元素,但是變長List可以添加元素。
//定義一個變長集合
scala> val list2=scala.collection.mutable.ListBuffer[Int]()
list2: scala.collection.mutable.ListBuffer[Int] = ListBuffer()
//添加元素
scala> list2+=2
res4: list2.type = ListBuffer(2)
//添加一個元組
scala> list2+=(3,4)
res5: list2.type = ListBuffer(2, 3, 4)
//添加一個集合
scala> list2++=List(5,6)
res6: list2.type = ListBuffer(2, 3, 4, 5, 6)
//減少一個元素
scala> list2-=3
res7: list2.type = ListBuffer(2, 4, 5, 6)
//減少一個集合
scala> list2--=List(5,4)
res8: list2.type = ListBuffer(2, 6)
(4) Set
● Set中的元素是無序不重複的集合。
● 定義一個Set集合:scala> var set=Set(1,2,3,4,5)
● 定義要給可變長Set集合:val set1=scala.collection.mutable.SetInt
● 對變長Set集合,+ 、+=、++=是不同的
set1+1是創建另外一個可變Set集合,內容爲添加了一個1。
set1+=1 是在原來的Set集合中添加一個元素1
(5) Map集合
● Map是鍵值對的集合
● 定義一個定長Map: scala> val map=Map(“zhangsan”->20,”lisi”-> 25)
● 獲取值
scala> map(“zhangsan”) 是根據key獲取值。
scala> map.getOrElse(“zhangsan”,0) 根據Key獲取值,如果是空則返回0。
● 賦值
同Set一樣,只有可變的Map集合才能賦值。
--定義一個可變的Map
scala> val b=scala.collection.mutable.Map("zs"->20,"lisi"->30)
b: scala.collection.mutable.Map[String,Int] = Map(lisi -> 30, zs -> 20)
--第一種賦值方式,添加一個wangwu
scala> b("wangwu")=35
--顯示當前b的內容
scala> b
res1: scala.collection.mutable.Map[String,Int] = Map(lisi -> 30, zs -> 20, wangwu -> 35)
--第二種,添加內容方式,用+=
scala> b+=("wangma"->35,"hanliu"->40)
--刪除內容,用-=
scala> b-="zs"
● 遍歷Map
//第一種遍歷方法
scala> for((key,value)<-b)
| println("key="+key+",value="+value)
key=lisi,value=30
key=wangma,value=35
key=hanliu,value=40
key=wangwu,value=35
//第二種遍歷方式
scala> for(ele <- b.keySet)
| println("key="+ele+",value="+b.getOrElse(ele,0))
key=lisi,value=30
key=wangma,value=35
key=hanliu,value=40
key=wangwu,value=35
//只遍歷值
scala> for(ele<-b.values)
| println("value="+ele)
value=30
value=35
value=40
value=35
//遍歷的另外一種方式,value不一定有值時
scala> for((key,_)<-b)
| println("key="+key+",value="+b.getOrElse(key,0))
key=lisi,value=30
key=wangma,value=35
key=hanliu,value=40
key=wangwu,value=35
6、 模式匹配
Java中只能對數值進行模式匹配,但是在Scala中,除了可以對值進行模式匹配外,還可以對類型進行模式匹配,對Array和List的元素進行匹配、對case class進行匹配、甚至對有值或者沒有值進行匹配。
(1) 模式匹配值
package scala.com.chybinmy.scala.spark
object MatchDemo {
def main(args: Array[String]) {
judge("A")
judge("E")
}
def judge(tag: String): Unit = {
tag match {
case "A" => println("Excellent")
case "B" => println("Good")
case "C" => println("Just so so")
case _ => println("You need word hard")
}
}
}
def judge(tag: String,name:String): Unit = {
tag match {
case "A" => println("Excellent")
case "B" => println("Good")
case "C" => println("Just so so")
case _ if name.equals("xiaoming")=>println(name+",come on!")
case _ => println("You need word hard")
}
}
(2) 匹配類型
package scala.com.chybinmy.scala.spark
object ExceptionTest extends App {
try {
val i = 1 / 0
}
catch {
case e: ArithmeticException => throw new RuntimeException("not zero!")
case e: Exception => println(e.getMessage)
}
finally {
println("finally")
}
}
當匹配類型時,case 後面的e爲變量名,冒號後面的是要匹配的類型名稱。
(3) 匹配classs
package scala.com.chybinmy.scala.spark
class Person
case class Teacher(name: String, subject: String) extends Person
case class Student(name: String, classroom: String) extends Person
object CaseMathTest {
def judge(person: Person): Unit = {
person match {
case Teacher(name, subject) => println("Teacher:" + name + "," + subject)
case Student(name, classroom) => println("Student:" + name + "," + classroom)
case _ => println("error!")
}
}
def main(args: Array[String]) {
judge(new Teacher("xuanyu","spark"))
judge(new Student("ming","7 ban"))
judge(null)
judge(new Person)
}
}
(4) 匹配Option
Scala中的Option是一種特殊的類型,Option有兩種值:Some表示有值,None表示沒有值。模式匹配Option用於判斷某個變量有值還是沒有值。
package scala.com.chybinmy.scala.spark
object OptionMathDemo {
def getGrade(name: String): Unit = {
val grades = Map("xx" -> "A", "yy" -> "B", "zz" -> "C")
val g = grades.get(name)
g match {
case Some(course) => println(name + ",your grade is " + course)
case None => println(name + ",no your grade")
}
}
def main(args: Array[String]) {
getGrade("xx")
getGrade("aa")
}
}
7、 異常處理
package scala.com.chybinmy.scala.spark
object ExceptionTest extends App {
try {
val i = 1 / 0
}
catch {
case e: ArithmeticException => throw new RuntimeException("not zero!")
case e: Exception => println(e.getMessage)
}
finally {
println("finally")
}
}
8、 高階函數
輸入參數的類型爲函數的函數爲高階函數(higher-order function)。
package scala.com.chybinmy.scala.spark
object HigherDemo {
//定義一個高階函數
def greetin(f: (String) => Unit, name: String): Unit = {
f(name)
}
def main(args: Array[String]) {
//定義一個函數賦值爲常量
val sayHelloFunc = (name: String) => println("Helllo!" + name)
//將函數類型的常量做爲參數,傳遞給高階函數
greetin(sayHelloFunc, "老王!")
//將一個匿名函數做爲參數傳遞給高階函數
greetin((name: String) => println(name + "好!"), "張sir")
//省略匿名函數參數的類型
greetin((name) => println(name + "hao"), "小明")
//如果只有一個參數,可以省略括號
greetin(name => println("早" + name), "小紅")
//List.map是個高階函數
var list = List(1, 2, 3, 4, 5)
println(list)
println(list.map((x: Int) => x + 1))
println(list.map(x => x + 1))
//只有一個參數,並函數體中只用到一個這個參數,可以用_表示
println(list.map(_ + 1))
}
}
將函數做爲參數傳遞給另外一個函數,是一種封裝。
9、 隱式轉換
(1) 一個例子
package scala.com.chybinmy.scala.spark
//定義一個特殊人羣類
class SpecialPerson(val name: String)
//定義一個學生
class Stu(val name: String)
//定義一個老人類
class Older(val name: String)
object ImplicitDemo {
def main(args: Array[String]) {
//將Stu類型隱式轉換爲SpecialPerson類型
val stu = new Stu("xiaoping")
val ticket = buySpecialTicker(stu)
println("Buy ticket:" + ticket)
//將Oler類型隱式轉換爲SpecialPerson類型
val older = new Older("laoli")
val ticket2 = buySpecialTicker(older)
println("Buy older ticket:" + ticket2)
}
//買票函數,這個函數定義輸入參數是SpecialPerson類型的
var ticketNumber = 0
def buySpecialTicker(p: SpecialPerson): String = {
ticketNumber += 1
"T-" + ticketNumber
}
//定義一個隱式轉換函數,定義了那些類型可以隱式轉換爲SpecialPerson
implicit def objectToSpecialPerson(obj: Object): SpecialPerson = {
if (obj.getClass == classOf[Stu]) {
val stu = obj.asInstanceOf[Stu]
new SpecialPerson(stu.name)
}
else if (obj.getClass == classOf[Older]) {
val older = obj.asInstanceOf[Older]
new SpecialPerson(older.name)
}
else
new SpecialPerson("未知名字")
}
}
這段程序是特殊人羣買票的例子,objectToSpecialPerson定義了Stu類和Older類可以隱式轉換爲SpecialPerson類,所以雖然buySpecialTicker函數的輸入參數爲SpecialPerson類型,但是調用時,可以傳遞Stu和Older類型的值進去,因爲自動做了隱式轉換。
隱式轉換允許手動指定將某種類型的對象轉換爲其他類型的對象,最核心的就是定義了隱式轉換函數。
隱式轉換的強大之處就在於可以在不知不覺中加強了現有類型的功能,也就是說可以爲某一個類定義一個加強版的類,並定義相互轉換,從而讓源類可以調用加強版類裏的方法。
(2) 作用域和導入
Scala默認會使用兩種隱式轉換:
● 一種是從源類型或者目標類型的伴生對象內,找隱式轉換函數
● 一種是當前程序的作用域內找隱式轉換函數。
如果其他兩種方式都找不到,那就要手動導入了。如:
var sqlContext=new SQLContext(sc)
import sqlContext.implicit._
(3) 隱式轉換的時機
當在如下情況下,會嘗試進行隱式轉換:
● 調用某個函數,但是給函數傳入的參數的類型與函數定義的接收類型不匹配。
● 使用某個類型的對象,調用某個方法,而這個方法並不存在於該類型時
● 使用某個類型的對象,調用某個方法,雖然該類型有這個方法,但是給方法傳入的參數類型與方法定義的參數類型不匹配時
10、 Option
Scala中有一種特殊的類型是Option,用來表示那種有肯能存在也肯能不存在的值,Option有兩種值,一種是Some,表示有值,一種是None,表示沒有值。
Option通常會用於模式匹配中,用於判斷某個變量有值或者沒有值,比Null更加簡潔
11、 Trait
Scala中的Trait是一種特殊的概念,類似於Java中的接口,在Trait中可以用來定義抽象方法。類可以使用extends關鍵字繼承trait,繼承後必須實現其中的抽象方法,實現時不需要使用override關鍵字,雖然Scala不支持對類進行多繼承,但是支持使用with來多重繼承trait。
trait還可繼承於trait。
這一篇博文是【大數據技術●降龍十八掌】系列文章的其中一篇,點擊查看目錄:大數據技術●降龍十八掌