scala程序開發入門


scala程序開發入門,快速步入scala的門檻:

1、Scala的特性:

A、純粹面向對象(沒有基本類型,只有對象類型)、Scala的安裝與JDK相同,只需要解壓之后配置環境變量即可;
B、Scala在安裝之前必須先安裝JDK,因為Scala的編譯結果是中間字節碼文件,它需要在JVM上運行,Scala可以調用Java類庫來完成某些功能;
C、Scala類似於python,一半面向過程一半面向對象,還可以基於shell的命令行進行操作,當然也可以像Java那樣先使用scalac編譯成中間字節碼之后再使用scala解釋執行它,注意保存的Scala源文件名稱必須以scala作為擴展名,且文件名必須與對象名完全匹配;
D、Scala類似於JS腳本,區分大小寫,以換行符為單位來定義語句,但是如果需要在同一行定義多條語句則必須使用分號隔開;
E、執行速度對比:C(C++)>Java(Scala)>Python(Ruby);
F、Scala數據類型:
Byte(1byte)、Short(2byte)、Int(4byte)、Long(8byte)、Float(4byte)、Double(8byte)、Char(2byte)、String(取決於字符數量)、Boolean(1byte)、Unit(相當於void)、Null(相當於null)、Nothing(所有類的子類型)、Any(所有基本類型和引用類型的根類型)、AnyVal(所有基本類型的超類型)、AnyRef(所有引用類型的超類型)。

//null值只能被推斷為Null類型,null代表空值,它可以被賦值給任何AnyRef類型的常量或變量
scala> var kk=null 
kk: Null = null

//定義Boolean類型
scala> val flag=true
flag: Boolean = true

//定義Char類型 
scala> val flag='A'
flag: Char = A

//小數默認推斷為Double類型
scala> val height=1.67 
height: Double = 1.67

//整數默認推斷為Int類型
scala> val age=30 
age: Int = 30

//定義字符串類型(字串常量必須使用雙引號),顯示的結果類型為String,它是java.lang.String的縮寫形式
scala> val name="mmzs" 
name: String = mmzs

//Int類型可以自動向上轉型為超類型AnyVal
scala> val age2:AnyVal=age
age2: AnyVal = 30
//String類型為引用類型,不能上轉型到AnyVal,因為它們之間沒有繼承關系
scala> val name2:AnyVal=name 
<console>:12: error: the result type of an implicit conversion must be more specific than AnyVal
val name2:AnyVal=name
^
//但是可以自動上轉型為超類型AnyRef,因為AnyRef是所有引用類型的超類型
scala> val name2:AnyRef=name 
name2: AnyRef = mmzs
//同樣的,Int為基本類型,也不能上轉型到AnyRef,因為它們之間沒有繼承關系
scala> val age2:AnyRef=age 
<console>:12: error: the result type of an implicit conversion must be more specific than AnyRef
val age2:AnyRef=age
^
//但是所有的基本類型(AnyVal)和引用類型(AnyRef)都可以自動上轉型為根類型Any    
scala> val kkm:Any=age
kkm: Any = 30
scala> val kkm:Any=name
kkm: Any = mmzs
scala> val kkm:Any=age2
kkm: Any = 100
scala> val kkm:Any=name2
kkm: Any = mmzs
scala>:quit //也可以直接:q

注意:
對任何Scala對象的顯式類型定義其前面都有一個以英文冒號作為前綴的類型標注,這是Scala的語法所決定的,如:val abc:Int=123、def getName():String={............}
8中基本類型都是scala核心包中的,scala包相當於Java中的java.lang包
Scala是強類型的語言,並不是弱類型語言,雖然你並沒有顯式指定變量的類型,但是Scala會根據你賦的值自動進行類型推斷,一旦推斷出變量的類型,后續使用該變量就必須符合類型兼容原則
定義變量或函數的通式是:val[var|def] varName|funName(...):type=value|{.....}

G、字面常量:
單引號:表示單個字符
雙引號:表示字符串
三雙引號:表示具備可換行的多行字符串

//三雙引號實例
scala> val info="""my name is mmzs
| my age is 30"""
info: String =
my name is mmzs
my age is 30    

H、字符串之間以及字符串與變量、表達式之間的拼接直接使用重載的加號("+")
I、Scala中沒有static的概念,所有處於object定義對象中的成員都是靜態的,入口方法main必須被定義在object定義的類中,同時源文件的名字必須與object定義的對象名同名,一個源文件中可以使用class定義多個類,
也可以使用object定義多個對象,但是作為運行的入口main方法永遠都是處於與源文件名相同的對象體內
J、Scala中的所有常量和變量(包括成員常量、變量和局部常量、變量)都必須先定義和初始化然后才能被使用,暫時不能確定值的常量、變量可以被初始化為空值(即0,初始化為0意味着數字類型被賦值為0,字符類型被賦值為空格、布爾類型被賦值為false,引用類型全部被賦值為null),如:

scala> var age:Int=0
age: Int = 0
scala> var height:Double=0
height: Double = 0.0
scala> var code:Char=0
code: Char = ?
scala> var flag:Boolean=false
flag: Boolean = false
scala> var name:String=null
name: String = null

2、基礎語法:

A、變量的聲明:

//聲明變量(變量的值可以被重新賦值以改變它的值)
var VariableName:DataType=Initial Value
//聲明常量(常量的值不可以被重新賦值,值不能被改變)
val VariableName:DataType=Initial Value

//val定義的是常量,不能被重新賦值,但是可以使用val重新定義它,如:
scala> val ab=20
ab: Int = 20 //自動推斷為Int類型
scala> ab=50 //此處常量ab不能被重新賦值
<console>:12: error: reassignment to val
       ab=50
         ^
scala> val ab=50 //但是可以重新定義該常量
ab: Int = 50

//var定義的是變量,可以被重新賦值,如:
scala> var ab=220
ab: Int = 220
scala> ab=50 //此處ab變量被重新賦值
ab: Int = 50
scala> var ab=50.05 //也可以被重新定義
ab: Double = 50.05

//val與var定義的常量或變量都可以相互被對方重定義,重定義時可以被定義成其它類型
scala> val ab=100
ab: Int = 100
scala> var ab=true
ab: Boolean = true
scala> val ab='A'
ab: Char = A     
scala> var ab=45.55
ab: Double = 45.55
scala> ab=true //變量的值可以被重新賦值,但是所賦的值必須與定義該變量時的類型匹配
<console>:12: error: type mismatch;
 found   : Boolean(true)
 required: Double
       ab=true
          ^
scala> ab='A' //隱式將Char類型按ASCII碼轉換成Double類型,這是符合自動類型轉換規則的ab: Double = 65.0    

B、變量的類型與賦值表達式

  在 Scala 中聲明變量和常量不一定要指明數據類型,在沒有指明數據類型的情況下,其數據類型是通過變量或常量的初始值來進行推斷,可以看出Scala語言實際上是一種強類型的編程語言,Scala聲明變量的語法有點類似於JS但與JS也有區別,區別在於JS中的var關鍵字不是必須的,且JS中的每一個變量可以自由靈活的被賦值為任何類型的值;但是Scala語言中的var或val關鍵字是必須的,且一旦使用這些關鍵字定義了變量或常量的類型(根據定義時的初始值進行類型自動推斷)之后,后續對該變量的賦值就必須符合類型兼容原則,如果該變量需要被賦值為其它類型則只能使用val或var對其重新定義

var myVar = 10;//聲明變量
val myVal = "Hello,Scala!";//聲明常量
//以上實例中,myVar 會被推斷為Int類型,myVal會被推斷為String類型
//使用枚舉法同時聲明多個相同類型和值的變量
scala> val a,b=100 //a,b都聲明初始值為100,都被推斷為Int類型
a: Int = 100
b: Int = 100
scala> a
res1: Int = 100
scala> b
res2: Int = 100
//使用元組法同時聲明多個不同類型和值的變量 
scala> val (a,b)=(100,"mmzs")
a: Int = 100
b: String = mmzs
//使用元組法時也可以顯式指定類型
scala> val (a:Int,b:String)=(100,"mmzs")
a: Int = 100
b: String = mmzs
//注意下面這種方式實際上是枚舉法的賦值方式,它表示將右邊的元組對象同時賦值給左邊的a和b兩個變量
scala> val a,b=(100,"mmzs")
a: (Int, String) = (100,mmzs)
b: (Int, String) = (100,mmzs)

C、Scala命令行換行
C1、可以使用三雙引號輸入多行數據
C2、如果輸入了錯誤的內容導致命令等待可以連續兩次回車以結束輸入

D、函數定義

def max(a:Int,b:Int):Int={
  ......
}

說明:
函數定義以def開始,函數參數名后面必須指定類型,因為Scala無法自動推斷出函數的參數類型;
函數如果被設計為遞歸函數(在函數體中調用它自身)則函數的返回類型就不能被省略,即遞歸函數無法推斷函數的返回類型;
函數體實際上也被稱之為函數塊,一個塊中如果只有一條語句則可以省略塊兩端的大括號,如:def max2(x: Int, y: Int) = if (x > y) x else y

//函數的調用與表達式的調用類似,如下:
scala> def max2(x: Int, y: Int) = if (x > y) x else y
max2: (x: Int, y: Int)Int
scala> max2(3,5)
res4: Int = 5
//函數也可以被推斷為Unit類型:
scala> def greet()=println("Hello, world!")
greet: ()Unit
//下面的函數定義返回Int類型,但是實際上函數被推斷為不返回任何值,即Unit類型,
scala> def greet():Int=println("Hello, world!")
<console>:11: error: type mismatch; //實際返回的類型與定義的返回類型不匹配
found : Unit //實際的返回類型
required: Int //定義的返回類型
def greet():Int=println("Hello, world!")

E、Scala腳本編寫與調用

#編寫Scala腳本:

[root@CloudDeskTop install]# vi test.scala
[root@CloudDeskTop install]# cat test.scala
//使用args數組接收傳入腳本的參數
println("Hello:"+args(0))
println("Hello:"+args(1))
[root@CloudDeskTop install]# scala test.scala liming zhangsan
Hello:liming
Hello:zhangsan

說明:
scala腳本(test.scala)中可以使用args數組來獲得傳入腳本(test.scala)的參數,注意取參數所用的是小括號,不是中括號;scala腳本中的注釋與Java中的注釋是相同的,使用雙斜杠標記單行注釋,使用/*......*/標記多行注釋。

F、while循環

F1、Scala中沒有++i、--i、i++、i--的運算模式,可以使用i=i+1、i+=1來予以代替
F2、Scala中默認一行一條語句,如果需要在同一行放置多條語句則應該使用英文分號隔開
F3、Scala與Java相同,if、switch、while、for等后面的條件表達式都需要使用小括號括起來,這與Python、Ruby是不同的

F、for循環

scala> val list=List("zhu","ge","liang")
list: List[String] = List(zhu, ge, liang)

scala> for(ele<-list) println(ele)
zhu
ge
liang
//說明:由於for循環體中就只有一條語句(println(arg))所以省略了大括號

for表達式中<-后面的參數是一個集合,前面的參數是一個迭代變量,該迭代變量是隱式的val聲明常量,在循環塊中不能改變它的值,但是每次迭代都將重新定義和初始化它的值

scala> for(ele<-list){ele="sdnj";println(ele)} //試圖在循環體中對它賦值是錯誤的
<console>:13: error: reassignment to val //因為它被指定為常量,盡管如此,你不能顯式的寫成for(val ele<-list){println(ele)},這是語法規定所需
for(ele<-list){ele="sdnj";println(ele)}

G、函數式編程(函數本身作為參數傳遞)

foreach迭代函數
foreach函數的參數接收一個函數對象,函數對象的=>符號左邊是參數列表,右邊是函數體,參數arg的類型沒有指定則通過Scala來自動推斷,而函數的返回類型沒有指定也是通過函數體的返回值來進行自動推斷,函數的參數函數是通過隱函數來定義的;

隱函數的特征:
A、隱函數的定義中沒有關鍵字def和函數名
B、隱函數的參數類型並不是必須的,它可以通過實參值進行自動類型推斷,這是與顯函數定義不同的地方
C、隱函數的參數列表中如果只有一個參數則沒有給定參數類型的情況下其參數兩端的小括號可以省略,甚至
可以直接省略掉參數的定義和賦值符號,直接形成偏函數的定義
D、隱函數無需定義返回類型,返回類型根據函數體自動推斷
D、函數體的賦值符號是=>,而不是=

[root@CloudDeskTop install]# vi test.scala
[root@CloudDeskTop install]# cat test.scala 
args.foreach(arg=>println(arg))
[root@CloudDeskTop install]# scala test.scala zhu ge liang
zhu
ge
liang

小結:
所有變量、常量、函數在定義時其類型(對於函數而言是返回類型)是可選的(如果顯式的給定類型則給定類型將作為自動化類型推斷結果的一種校驗),對於函數的參數類型分兩種情況,在顯式定義一個命名函數時其參數類型不可省略,在函數式編程領域中其參數函數的參數類型則是可選的(同樣,如果顯式的給定類型則給定類型將作為自動化類型推斷結果的一種校驗)

//如果需要顯式指定參數類型則需要使用小括號將參數列表括起來:
scala> val list=List("zhu","ge","liang")
list: List[String] = List(zhu, ge, liang)
scala> list.foreach((ele:String)=>println(ele))//注意foreach不能對ArrayList集合遍歷
zhu
ge
liang

//如果存在多個參數則函數定義格式如下:
(key:String,value:Object)=>println(key+":"+value)

小結:
在Scala中任何類型的變量在定義時都必須初始化(包括類),類在定義時使用類體初始化類的定義(但在Scala中官方並不認為這被稱為初始化,因為它沒有賦值符號),

函數在定義時使用函數體進行初始化,賦值符號是=(顯函數定義)或=>(隱函數定義),常量和變量在定義時使用常量值或變量值進行初始化,賦值符號是=

//如果只有一個參數則可以省略參數定義和箭頭部分(這種書寫方式被稱為偏應用函數)
scala> list.foreach(println)
zhu
ge
liang

注意:實際上foreach函數是for循環的變體,本質上都是相同的迭代方式,for循環的可讀性更高,但foreach函數是標准的函數式編程寫法

H、數組

H1、通用方式創建數組和使用數組

//當沒有指定泛型參數時默認數組元素類型為Nothing類型,與Any類型相反,Nothing代表所有類的子類型,這意味着任何類型的數據不經過強制下轉型將無法放入數組中去

//小括號中的參數2代表數組的長度(即數組中元素的個數),Scala根據此參數初始化數組的長度,這與Java不同,Scala中初始化數組的長度、下標的訪問都是使用小括號,而不是方括號

scala> var arr=new Array(2);
arr: Array[Nothing] = Array(null, null)

scala> arr(0)="zhugeliang"
<console>:13: error: type mismatch;
found : String("zhugeliang")
required: Nothing
arr(0)="zhugeliang"
^

注意: 從上面實例可以發現使用數組長度來初始化Scala數組時必須指定其泛型,否則數組中將無法放入任何元素

//指定了泛型之后就可以直接放入元素了
scala> var arr=new Array[String](3);
//如果需要顯式指定變量類型可以像下面這樣定義,可以看到泛型實際上是類型的一部分
scala> var arr:Array[String]=new Array[String](3);
arr: Array[String] = Array(null, null, null)

scala> arr(0)="mmzs"
scala> arr(0)
res1: String = mmzs
scala> arr(1)="淼淼之森"
scala> arr(2)="mmzsblog"

//foreach並傳遞隱函數遍歷
scala> arr.foreach(ele=>println(ele))
mmzs
淼淼之森
mmzsblog

//foreach並傳遞偏函數遍歷
scala> arr.foreach(println)
mmzs
淼淼之森
mmzsblog

//使用for循環遍歷
scala> for(ele<-arr)println(ele)
mmzs
淼淼之森
mmzsblog

//使用for循環並構建下標集合遍歷,to關鍵字實際上是一個帶一個Int參數的方法,0 to 2被解釋成(0).to(2),to方法返回的是一個序列,arr.length描述集合的長度
scala> for(i<-0 to arr.length-1)println(arr(i))
mmzs
淼淼之森
mmzsblog

說明:
從技術上講,Scala中沒有操作符重載的概念,所有的操作符都將被視為方法然后實現對方法的調用,這也是因為Scala中的數組實際上就是一個普通Scala類的實現而已,
對數組類長度和下標的訪問都是對該類中相應方法的調用,這也是下標的訪問為什么是小括號而不是方括號的原因,比如對算術運算符+、-、*、/的訪問也是視為方法來調用的:

scala> 1+2
res23: Int = 3
scala> 1.+(2)
res24: Int = 3
scala> (1).+(2)
res25: Int = 3

這個原則不僅僅局限於數組:任何對某些在括號中的參數的對象的應用將都被轉換為對工廠方法apply(工廠方法apply是一個帶可變長度參數的方法)的調用。當然前提是這個類型實際定義過apply方法。
所以這是一個通則,上面對數組的讀寫過程實際上被解釋為:

scala> var arr:Array[String]=new Array[String](3);
arr: Array[String] = Array(null, null, null)
scala> arr.update(0,"zhu")
scala> arr.update(1,"ge")
scala> arr.update(2,"liang")
scala> for(i<-0.to(2))println(arr.apply(i))
zhu
ge
liang

H2、簡潔方式創建數組和使用數組

//注意這種模式下不能再使用new關鍵字,同時也無需指定數組的泛型和長度,因為在創建數組的同時已經用實際的元素類型和值初始化了

scala> val arr=Array("zhu","ge","liang")
arr: Array[String] = Array(zhu, ge, liang)

scala> arr.apply(0)
res8: String = zhu
scala> arr.apply(1)
res9: String = ge

//也可以直接使用apply方法來創建數組(這種調用是直接對底層工廠方法的調用,效率或許更高,但是代碼表現得有點啰嗦)

scala> val arr=Array.apply("zhuzhu","gege","liangliang")
arr: Array[String] = Array(zhuzhu, gege, liangliang)

scala> arr.apply(1)
res10: String = gege

scala> arr(1)
res11: String = gege
//由於數組元素的類型全部都是些基本類型,於是數組元素的類型被推斷為AnyVal類型
scala> val arr=Array.apply(36,1.67,'A',true)
arr: Array[AnyVal] = Array(36, 1.67, A, true)

//由於數組元素的類型全部都是些引用類型,於是數組元素的類型被推斷為Object類型,在Java平台上,AnyRef是java.lang.Object類的別名,在.Net平台上,AnyRef是system.Object類的別名
scala> class User{}
defined class User

scala> val arr=Array.apply("zhugeliang",new User())
arr: Array[Object] = Array(zhugeliang, User@c9cd85d)

//由於初始化指定的類型既有引用類型又有基本類型,所以數組元素的類型被推斷為Any類型
scala> val arr=Array.apply("liubei",39,1.77)
arr: Array[Any] = Array(liubei, 39, 1.77)

//使用簡潔方式創建數組時最好不要加泛型,這樣可以使得Scala完成自動泛型推斷,如果自定義了泛型則要求數組中的元素全部與泛型兼容,否則將拋出類型不匹配的異常
scala> val arr=Array[String]("ligang",23)
<console>:11: error: type mismatch;
found : Int(23)
required: String
val arr=Array[String]("ligang",23)
^

小結:
使用new關鍵字創建數組時,小括號中的參數是唯一的,且是必選參數,而且必須是一個代表數組長度的整型數據,使用簡潔方式創建數組時小括號中的參數是0到多個可選的參數值,這些數據是數組中的元素值,由於創建數組時沒有指定泛型則默認泛型為Nothing類型,所以對於泛型而言有以下建議:
  a、如果使用new方式創建數組則必須指定泛型,否則后續無法為Nothing類型的數組元素賦值,使用new方式創建數組適合於數組中元素較多無法一次性枚舉出來的情況使用
  b、如果使用簡潔方式創建數組則建議使用Scala的自動類型推斷,但你也可以強制指定泛型,以保證初始化的枚舉值類型都是准確無誤的,使用簡潔方式創建數組適合於數組中元素較少可以一次性枚舉出來的情況使用

I、列表List

Scala中的List(全名是scala.List)不同於Java中的java.util.List類型,Scala中的List與字符串String的操作類似,它是一種完全不可變的列表,任何對列表的增、刪、改操作都將產生一個新的列表對象返回,需要注意的是List與數組類型Array也不見得完全相同,雖然他們的長度都是不可以改變的,但是數組中的元素值則是可以改變的,而List集合中的元素值不能被改變,一旦改變將產生新的列表對象
a、合並兩個列表並返回一個新的列表

//創建兩個列表,泛型根據初始化的元素類型自動推斷為Any
scala> val myinfo=List("zhugeliang",39,1.77)
myinfo: List[Any] = List(zhugeliang, 39, 1.77)
scala> val youinfo=List("lingang",50,1.68)
youinfo: List[Any] = List(lingang, 50, 1.68)
//合並兩個列表中的元素(按順序合並)
scala> val info=myinfo:::youinfo
info: List[Any] = List(zhugeliang, 39, 1.77, lingang, 50, 1.68)
//合並后產生新的列表,地址不再相同
scala> myinfo==info
res1: Boolean = false
scala> youinfo==info
res2: Boolean = false

b、在列表的首部插入數據,返回一個新的列表

scala> val list=List(68,86,99)
list: List[Int] = List(68, 86, 99)

scala> val list2=100::list
list2: List[Int] = List(100, 68, 86, 99)

scala> list==list2
res14: Boolean = false

說明:
不能使用new關鍵字實例化List對象,因為List.apply()方法被定義在scala.List伴生對象上;
以冒號結尾的方法名表示調用關系上的反轉,即調用方法的對象是右邊的操作數,像上面的myinfo:::youinfo表示的調用關系是youinfo.:::(myinfo),而100::list表示的調用關系是list.::(100),如:

scala> list2.::(200)
res15: List[Int] = List(200, 100, 68, 86, 99)

scala> info.:::(myinfo)
res16: List[Any] = List(zhugeliang, 39, 1.77, zhugeliang, 39, 1.77, lingang, 50, 1.68)

而那些沒有以英文冒號結尾的關鍵字表示的方法名則調用方法的對象是左邊的操作數

c、串聯離散數據成列表對象

//使用Nil關鍵字可以創建一個空列表
scala> Nil
res15: scala.collection.immutable.Nil.type = List()
scala> val list3="ligang"::27::1.67::Nil
list3: List[Any] = List(ligang, 27, 1.67)
//也可以使用成員運算符調用方法,但是表現的很啰嗦
scala> val list4=Nil.::("liubei").::(36).::(1.67)
list4: List[Any] = List(1.67, 36, liubei)

d、列表元素訪問

//訪問列表中索引為2的元素值
scala> list4(2)
res16: Any = liubei
//計算列表的尺寸,length屬性幾乎是所有集合所具備的屬性,它用於表針集合的尺寸
scala> list4.length
res18: Int = 3
//返回列表的第一個元素或最后一個元素
scala> info.head
res19: Any = liubei
scala> info.last
res20: Any = 1.77
//列表是否為空列表
scala> Nil.isEmpty
res21: Boolean = true
scala> info.isEmpty
res22: Boolean = false
//返回除去第一個元素后的列表
scala> List(10,20,30,40).tail
res28: List[Int] = List(20, 30, 40)
//返回除去最后一個元素后的列表
scala> List(10,20,30,40).init
res27: List[Int] = List(10, 20, 30)
//刪除左邊的兩個元素
scala> info
res43: List[String] = List(lingang, wangfang, changhua, zhangjin, guanyu)
scala> info.drop(2)
res46: List[String] = List(changhua, zhagjin, guanyu)
//刪除右邊的兩個元素
scala> info.dropRight(2)
res47: List[String] = List(lingang, wangfang, changhua)
//反轉元素列表
scala> List(10,20,30,40).reverse
res29: List[Int] = List(40, 30, 20, 10)
//轉換列表為字符串
/*
mkString函數有兩次重載:
(sep: String)String 使用一個分隔符串接列表中的所有元素為一個字符串
(start: String,sep: String,end: String)String 使用一個分隔符串接列表中的所有元素為一個字符串,同時在這個字串的起始和結束位置追加第一個參數字串和最后一個參數字串
*/
scala> List(10,20,30,40).mkString(",")
res39: String = 10,20,30,40
scala> List(10,20,30,40).mkString("(","|",")")
res42: String = (10|20|30|40)

e、列表過濾處理:

scala> val info=List("lingang","wangfang","changhua","zhangjin","guanyu")
info: List[String] = List(lingang, wangfang, changhua, zhangjin, guanyu)
/*
e1、count函數原型是:(p: Any => Boolean)Int,將集合中每一個元素放入隱函數計算,並返回集合中滿足條件(參數函數返回true)的元素數量 
*/
//使用隱函數計算info集合中元素的字符數為6的元素個數
scala> info.count(s=>6==s.length)
res26: Int = 1
//使用偏函數計算info集合中元素的字符數為6的元素個數
scala> info.count(6==_.length)
res1: Int = 1

/*
e2、exists函數原型是:(p: Any => Boolean)Boolean,只要集合中有一個元素滿足條件(參數函數返回true)則exists函數返回true
*/
//判斷集合info中是否存在元素的值為changhua的元素
//使用隱函數計算
scala> info.exists(s=>s=="changhua")
res3: Boolean = true
scala> info.exists(s=>s=="changshad")
res4: Boolean = false
//使用偏函數計算
scala> info.exists(_=="changhua")
res6: Boolean = true

/*
e3、forall函數原型是:(p: Any => Boolean)Boolean,集合中所有元素滿足條件(參數函數返回true)則exists函數返回true;
該函數與exists函數是互補的,exists函數是描述or的關系,而forall是描述and的關系
*/
//判斷集合info中的元素類型是否都是String類型
scala> info.forall(s=>s.getClass().getSimpleName()=="String")
res12: Boolean = true
scala> info.forall(_.getClass().getSimpleName()=="String")
res14: Boolean = true
scala> info.forall(s=>s.getClass().getSimpleName()=="Integer")
res13: Boolean = false
scala> List("lingang","chenghua").forall(_.getClass().getSimpleName()=="String")
res15: Boolean = true
scala> List("lingang","chenghua",50).forall(_.getClass().getSimpleName()=="String")
res16: Boolean = false
scala> List("lingang","chenghua").forall(_.getClass().getName=="java.lang.String")
res17: Boolean = true
scala> List("lingang","chenghua",100).forall(_.getClass().getName=="java.lang.String")
res19: Boolean = false

說明:
在Scala中比較字符串是否相同可以使用雙等號,當然你使用equals方法來比較也是OK的:
scala> List("lingang","chenghua").forall(_.getClass().getName.equals("java.lang.String"))
res20: Boolean = true

/*
e4、filter函數原型是:(p: Any => Boolean)List[Any],過濾出滿足條件(參數函數返回true)的所有元素並返回這些元素組成的子列表
*/
//過濾出字串長度為6的所有元素組成的子列表
scala> info.filter(s=>s.length==6)
res7: List[String] = List(guanyu)
scala> info.filter(_.length==6)
res9: List[String] = List(guanyu)

/*
e5、map函數原型是:(f: Any => B)(implicit bf: scala.collection.generic.CanBuildFrom[List[Any],B,That])That,將集合中的每一個元素使用map的參數函數處理並收集參數函數處理后的返回值組成的列表
*/
scala> List(10,20,30,40).map(s=>s+5)
res35: List[Int] = List(15, 25, 35, 45)
scala> List(10,20,30,40).map(_+5)
res37: List[Int] = List(15, 25, 35, 45)

/*
e6、foreach函數原型是:(f: Any => U)Unit,將集合中的每一個元素放入參數函數中去並調用參數函數處理,參數函數的返回類型根據參數函數體自動推斷,foreach函數無返回值
*/
scala> info.foreach(s=>println(s))
lingang
wangfang
changhua
zhangjin
guanyu
scala> info.foreach(println(_))
lingang
wangfang
changhua
zhangjin
guanyu

J、元素Tuple

  元組與列表相同也是不可改變的,任何對元組的增、刪、改都將返回新的元組對象,元組與列表不同的是列表List中的類型都是相同的,使用列表時為了能夠在其中容納不同類型的元素,我們不得不使用與這些元素兼容的超類型,如:AnyVal、AnyRef或Any類型等,但是元組Tuple中的元素類型可以是不相同的,元組Tuple的實際類型取決於元組中元素的數量和各個元素的類型。
a、通用方式創建元組

//元組的泛型被自動推斷為Tuple2[String,Int],下面的這條語句存在兩次類型推斷過程,首先是推斷右邊Tuple2的泛型為Tuple2[String,Int],然后在推斷左邊info變量的類型為Tuple2[String,Int]
//TupleX中的X代表元組中元素的數量,元組的泛型由元組中的各個元素類型來確定,元組中的元素數量和類型決定了元組自身的類型
scala> val info=new Tuple2("liubei",28)
info: (String, Int) = (liubei,28)
//訪問元組的第一個元素
scala> info._1
res54: String = liubei
//訪問元組的第二個元素
scala> info._2
res55: Int = 28

說明:
  不能像訪問List中元素那樣去訪問元組中的元素,因為List的apply方法返回的類型都是同一類型,然而元組的各個元素的類型不見得完全相同,訪問元組中的元素只能使用成員運算符(.),其中元素格式為_N,N代表元素在元組中的下標

//顯式指定元組的泛型,但是左邊的info變量的類型需要Scala自動推斷
scala> val info=new Tuple2[String,Int]("ligang",28)
info: (String, Int) = (ligang,28)
//顯式指定變量的類型和元組的泛型
scala> val info:Tuple2[String,Int]=new Tuple2[String,Int]("ligang",28)
info: (String, Int) = (ligang,28)

b、簡潔方式創建元組(無new關鍵字)

scala> val info=Tuple2("ligang",28)
info: (String, Int) = (ligang,28)
//也可以顯式指定泛型和變量的類型
scala> val info=Tuple2[String,Int]("ligang",28)
info: (String, Int) = (ligang,28)
scala> val info:Tuple2[String,Int]=Tuple2[String,Int]("ligang",28)
info: (String, Int) = (ligang,28)

c、最簡方式創建元組(無Tuple關鍵字)

//info變量自動被推斷為:Tuple2[String,Int]類型
scala> val info=("ligang",28)
info: (String, Int) = (ligang,28)
//也可以顯式為變量指定類型
scala> val info:Tuple2[String,Int]=("ligang",28)
info: (String, Int) = (ligang,28)

小結:
  元組Tuple具有比列表List更高一個層級的不可變型,元組Tuple相對於列表List的優勢就是它的元素類型可以是不同的,然而元組Tuple的缺點是沒有像列表List那樣豐富的API操作,元組中的數據幾乎是固定不變的。

K、Set集合

  在Scala中,Set集合分為可變集合和不可變集合,其中的可變集合與Java中的Set集合是相似的,Set在Scala中被定義為一個Trait,它的具體實現是HashSet,HashSet仍然分為可變的HashSet和不可變的HashSet,所有可變的集合放置於scala.collection.mutable包中,而所有不可變的集合則放置於scala.collection.immutable包中,Set集合中的+=運算符在可變與不可變的具體集合實現中有着不同的實現,在可變的Set集合中,該運算相當於append,即直接往當前集合中追加元素,而在不可變的Set集合中,該運算相當於+產生的合並運算,這將導致產生新的Set集合對象,同時將合並后的新Set集合對象賦值給左邊的變量;Set集合中的元素是自動去重的,即沒有重復的元素,這個特征不同於Array和List;創建任何Set集合(包括HashSet集合)都只能使用簡潔模式,不能使用new的方式來創建,從此你會發現規避使用new來創建對象在Scala中是一種更好的通用選擇。

//創建一個Set集合,泛型被自動推斷為Any類型,左邊的set變量被自動推斷為Set[Any]類型
scala> val set=Set("ligang",28,1.67)
set: scala.collection.immutable.Set[Any] = Set(ligang, 28, 1.67)
//也可以顯式指定泛型和變量類型
scala> val set=Set[Any]("ligang",28,1.67)
set: scala.collection.immutable.Set[Any] = Set(ligang, 28, 1.67)
//追加一個元素到Set集合將返回一個新的Set集合對象
scala> val set02=set+"chenggang"
set02: scala.collection.immutable.Set[Any] = Set(ligang, 28, 1.67, chenggang)
scala> set==set02
res3: Boolean = false
//如果你希望將產生的新的Set集合對象再賦值回原來的變量,類似於字符串一樣的去改變原有變量,可以使用+=運算符,由於需要改變原有變量的引用值,所以需要使用var來定義該變量,而不是val
scala> var set=Set("ligang",28,1.67)
set: scala.collection.immutable.Set[Any] = Set(ligang, 28, 1.67)
//下面的這步操作相當於:set=set+"chenggang",這顯然是改變了原有set變量的值,這也是上面為什么要使用var來定義它的原因所在
scala> set+="chenglong"
scala> set
res5: scala.collection.immutable.Set[Any] = Set(ligang, 28, 1.67, chenglong)
//再次追加相同的元素則不會追加成功
scala> set+="chenglong"
scala> set
res3: scala.collection.immutable.Set[Any] = Set(ligang, 28, 1.67, chenglong)

說明:
上面的操作也許會考慮到使用val重新定義set變量(而堅持不使用var),但這種情況會發生遞歸錯誤,所以我們只能使用另一個變量set02來替換,或者使用var定義它然后使用+=運算符

scala> val set=set+"zhangsan"
<console>:12: error: recursive value set needs type
val set=set+"zhangsan"
^
從上面的Set集合創建過程可以看出Set集合默認創建為不可變的集合類型,如果你需要的是可變的集合類型則必須使用import顯式的導入可變類型的Set
scala> import scala.collection.mutable.Set
import scala.collection.mutable.Set
scala> val set=Set("ligang",28,1.67)
set: scala.collection.mutable.Set[Any] = Set(ligang, 1.67, 28)
//下面這句相當於append操作,直接改變集合對象本身,並不會產生新的集合對象
scala> set+="zhangsan"
res5: set.type = Set(ligang, 1.67, 28, zhangsan)
//再次追加相同的元素值則不會追加成功
scala> set+="zhangsan"
res6: set.type = Set(ligang, 1.67, 28, zhangsan)

/*
如果你想基於特定的實現類創建Set集合則必須使用import導入相應的類型,但實際生產上用的較少
*/
//導入可變的HashSet類型
scala> import scala.collection.mutable.HashSet
import scala.collection.mutable.HashSet
scala> val set=HashSet("ligang",28,1.67)
set: scala.collection.mutable.HashSet[Any] = Set(ligang, 1.67, 28)
//創建不可變的HashSet類型
scala> val set=scala.collection.immutable.HashSet("ligang",28,1.67)
set: scala.collection.immutable.HashSet[Any] = Set(28, ligang, 1.67)

Set小結:
對於不可變的Set集合類型變量應該使用var來進行定義以保證追加元素后產生的新集合對象能夠被賦值回原來的變量,對於可變的Set集合類型通常是使用val來進行定義,此時追加元素將改變集合本身,而不會產生任何新的集合對象。

Array、List、Set、Tuple小結:
尺寸的可變性:
Array和Tuple是在實例化時定義長度的,實例化之后不可以改變它的長度;而List、Set則可以動態改變長度,如List可以通過::追加元素產生新的List對象,而Set可以通過+=追加元素產生新的Set對象或者直接改變集合本身;
集合中元素類型是否相同:
Array、List、Set集合中元素類型相同,Tuple集合中的元素類型可以相同、也可以不同;
集合中的元素值是否可以改變:
Array集合中的元素值可以改變,List和Tuple集合中的元素值不能被改變,Set集合中的元素有可變和不可變兩種實現(分別使用val和var來定義);
元素是否可以重復:
Set集合中的元素不可以重復,Array、List、Tuple集合中的元素值可以重復;
泛型推斷原理:
對於不可變集合而言必須在實例化時指定各個元素的值,此時Scala可以自動根據給定的元素值來推斷其集合的泛型;而對於可變集合則可以延遲給定各個元素的值,此時的可變集合泛型在實例化時就會被自動推斷為Nothing類型,此后需要添加到可變集合的各個元素不得不強制下轉型為Nothing,否則將無法添加元素到可變集合中,對於Array之前我們就已經看到了,現在來看看可變集合Set,它將與Array遇到同樣的情況:

scala> import scala.collection.mutable.Set
import scala.collection.mutable.Set

scala> val set=Set()
set: scala.collection.mutable.Set[Nothing] = Set()

scala> set+="ligang"
<console>:14: error: type mismatch;
 found   : String("ligang")
 required: Nothing
       set+="ligang"
            ^

L、Map集合

a、->與<-的區別:
前者是被定義在Any根類中的方法,即任何對象調用該方法都將返回一個二元素元組,該元素中的第一個元素是當前對象,第二個元素是參數對象

scala> "name"->"ligang"
res1: (String, String) = (name,ligang)
scala> "age"->28
res2: (String, Int) = (age,28)
scala> "height"->1.64
res3: (String, Double) = (height,1.64)
scala> 100->"zhanghua"
res4: (Int, String) = (100,zhanghua)
//也可以使用成員運算符來調用該方法
scala> 150.->("zhanghua")
res5: (Int, String) = (150,zhanghua)

后者是被用在for循環中遍歷集合之用:

scala> val info=List(20,60,80)
info: List[Int] = List(20, 60, 80)
scala> for(e<-info)println(e)
20
60
80

b、創建不可變的Map對象
Map集合中下標被稱之為Key,元素值被稱之為Value,Map集合中的每一個元素被稱之為一個鍵值對的對象,這個鍵值對是一個包含了Key和Value的二元素元組

//首先創建三個鍵值對元組對象,它們是entry1、entry2和entry3
scala> val entry1="name"->"ligang"
entry1: (String, String) = (name,ligang)
scala> val entry2="age"->36
entry2: (String, Int) = (age,36)
scala> val entry3="height"->1.67
entry3: (String, Double) = (height,1.67)
//再創建不可變的Map對象,注意對不可變的Map應該使用var來定義,否則后續無法使用+=運算符追加鍵值對
scala> var info=Map(entry1,entry2,entry3)
info: scala.collection.immutable.Map[String,Any] = Map(name -> ligang, age -> 36, height -> 1.67)
//訪問Map集合中的元素應根據Key來訪問Value
scala> info("name")
res8: Any = ligang
scala> info("age")
res9: Any = 36
//追加鍵值對
val entry3="height"->1.67
scala> val info2=info+entry4
info2: scala.collection.immutable.Map[String,Any] = Map(name -> ligang, age -> 36, height -> 1.67, weight -> 118)
scala> info==info2
res13: Boolean = false
//也可以使用+=來追加鍵值對
scala> val entry5="birthday"->java.sql.Date.valueOf("1982-12-26")
entry5: (String, java.sql.Date) = (birthday,1982-12-26)
scala> info+=entry5
scala> info
res15: scala.collection.immutable.Map[String,Any] = Map(name -> liyongfu, age -> 36, height -> 1.67, birthday -> 1992-12-26)

c、創建可變的Map對象

scala> import scala.collection.mutable.Map
import scala.collection.mutable.Map
//注意對可變的Map應該使用val來定義,否則后續無法使用+=運算符追加鍵值對
scala> val info=Map(entry1,entry2,entry3) info: scala.collection.mutable.Map[String,Any] = Map(age -> 36, name -> ligang, height -> 1.67) scala> info+=entry5 res16: info.type = Map(birthday -> 1992-12-26, age -> 36, name -> ligang, height -> 1.67) scala> info res17: scala.collection.mutable.Map[String,Any] = Map(birthday -> 1992-12-26, age -> 36, name -> ligang, height -> 1.67)

說明:
Scala中的集合類型默認引用的是不可變的集合類型,如果需要使用可變的集合類型則需要手動使用import導入scala.collection.mutable._(這里的下划線相當於java中的*),如:

scala> import scala.collection.mutable._
import scala.collection.mutable._
scala> val info=Map[String,Any]()
info: scala.collection.mutable.Map[String,Any] = Map()
//直接追加鍵值對
scala> info+="name"->"ligang"
res0: info.type = Map(name -> ligang)
scala> info+="age"->28
res1: info.type = Map(age -> 28, name -> ligang)

在Java中通常創建的集合對象都是可變的,同時如果引用集合對象的變量沒有使用final修改則該變量也是可變的,如果在Scala中需要像Java中的那種集合創建方式則應該是如下情況:

scala> import scala.collection.mutable._
import scala.collection.mutable._
scala> var info=Map[String,Any]()
info: scala.collection.mutable.Map[String,Any] = Map()

M、函數式風格編程與文件操作

M1、var與val的選擇

  通常情況下,如果使用var定義來實現編程,那么這種風格就是Scala中的指令式風格,指令式風格對於C/C++以及Java程序員都是司空見慣的一種模式,如果需要向函數式風格化推進,則應該盡可能的使用val編程,而不再是使用var來編程;其二,通常我們在編程中應該首先思考的是Scala的API,當我們考慮到有難度時再回退到Java的API中來實現,這是一種不錯的思路。

M2、文件操作

//構建測試文件

[root@CloudDeskTop install]# vi testfile
[root@CloudDeskTop install]# cat testfile 
01    lingang    2009-12-28
02    zhanghua    1998-10-12
03    chengqiang    1923-11-18
04    歡迎來到mmzs
05    覺得有用的話
06    點個贊唄

//編寫腳本

[root@CloudDeskTop install]# vi testreadfile
[root@CloudDeskTop install]# cat testreadfile 
import scala.io._
if(args.length<=0){
println("args number is error...")
java.lang.System.exit(1)
}
val fileName=args(0)
val source:BufferedSource=Source.fromFile(fileName)
val its:Iterator[String]=source.getLines
for(line<-its) println(line.length+"=>"+line)

//調用腳本讀取文件內容並打印到控制台

[root@CloudDeskTop install]# scala testreadfile testfile
27=>01    lingang    2009-12-28
28=>02    zhanghua    1998-10-12
30=>03    chengqiang    1923-11-18
14=>04    歡迎來到mmzs
12=>05    覺得有用的話
10=>06    點個贊唄

說明:
val its:Iterator[String]=source.getLines返回的是一個迭代器,迭代器指針指向磁盤文件的第一行,每循環一次都將從磁盤上讀取下一行的數據到內存中;
如果需要一次性將磁盤文件中的內容讀取到內存可以使用迭代器調用toList方法:
val its:Iterator[String]=source.getLines
val allLines:List[String]=its.toList

//代碼如下
[root@CloudDeskTop install]# cat testreadfile 
import scala.io._
if(args.length<=0){
    println("args number is error...")
    java.lang.System.exit(1)
}
val fileName=args(0)
val source:BufferedSource=Source.fromFile(fileName)
val its:Iterator[String]=source.getLines
//for(line<-its) println(line.length+"=>"+line)
//從此行開始修改 val allLines:List[String]=its.toList println(allLines.length+"=>"+allLines) //結果如下 [root@CloudDeskTop install]# scala testreadfile testfile 6=>List(01 lingang 2009-12-28, 02 zhanghua 1998-10-12, 03 chengqiang 1923-11-18, 04 歡迎來到mmzs, 05 覺得有用的話, 06 點個贊唄)

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM