scala官方網址:html
Scala是一種多範式的編程語言,其設計的初衷是要集成面向對象編程和函數式編程的各類特性。Scala運行於Java平臺(Java虛擬機),併兼容現有的Java程序。http://www.scala-lang.orgjava
一、優雅:這是框架設計師第一個要考慮的問題,框架的用戶是應用開發程序員,API是否優雅直接影響用戶體驗。react
二、速度快:Scala語言表達能力強,一行代碼抵得上Java多行,開發速度快;Scala是靜態編譯的,因此和JRuby,Groovy比起來速度會快不少。程序員
三、能融合到Hadoop生態圈:Hadoop如今是大數據事實標準,Spark並非要取代Hadoop,而是要完善Hadoop生態。JVM語言大部分可能會想到Java,但Java作出來的API太醜,或者想實現一個優雅的API太費勁。 es6
由於Scala是運行在JVM平臺上的,因此安裝Scala以前要安裝JDK。正則表達式
訪問Scala官網http://www.scala-lang.org/下載Scala編譯器安裝包,目前最新版本是2.12.x,這裏下載scala-2.11.8.msi後點擊下一步就能夠了(自動配置上環境變量)。也能夠下載scala-2.11.8.zip,解壓後配置上環境變量就能夠了。算法
下載Scala地址https://www.scala-lang.org/download/2.11.8.htmlapache
而後解壓Scala到指定目錄編程
tar -zxvf scala-2.11.8.tgz -C /usr/java
配置環境變量,將scala加入到PATH中
vi /etc/profile export JAVA_HOME=/usr/java/jdk1.8 export PATH=$PATH:$JAVA_HOME/bin:/usr/java/scala-2.11.8/bin |
目前Scala的開發工具主要有兩種:Eclipse和IDEA,這兩個開發工具都有相應的Scala插件,若是使用Eclipse,直接到Scala官網下載便可http://scala-ide.org/download/sdk.html。
因爲IDEA的Scala插件更優秀,大多數Scala程序員都選擇IDEA,能夠到http://www.jetbrains.com/idea/download/下載,點擊下一步安裝便可,安裝時若是有網絡能夠選擇在線安裝Scala插件。
這裏咱們使用離線安裝Scala插件:
1.安裝IDEA,點擊下一步便可。
2.下載IEDA的scala插件
插件地址: https://plugins.jetbrains.com/plugin/1347-scala
3.安裝Scala插件:Configure -> Plugins -> Install plugin from disk -> 選擇Scala插件 -> OK -> 重啓IDEA
REPL ==> 交互式解析器環境
R(read)、E(evaluate) 、P(print)、L(loop)
輸入值,交互式解析器會讀取輸入內容並對它求值,再返回結果,並重復此過程。(所見即所得)
REPL特性:
變量在會話週期內一直可用
多行代碼和單行代碼一塊兒編譯
支持連接外部庫和代碼
REPL歷史命令跨會話存儲
在命令行輸入scala以啓動scala REPL
File ==> New ==> Project
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.11.8</version>
<!-- 若是想要用java -jar 來運行咱們打包以後的jar包,則下面這個配置必須註釋掉 -->
<!-- <scope>provided</scope>-->
</dependency>
</dependencies>
<build>
<plugins>
<!-- 限制jdk版本插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- 編譯scala須要用到的插件 -->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 項目打包用到的插件 -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>cn.itcast.scala.demo1.ScalaFirst</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
src ==> main ==> new ==> Directory ==> scala
開發代碼以下:
object ScalaFirst {
def main(args: Array[String]): Unit = {
println("hello world")
}
}
將咱們的代碼打包,以後,進行運行
雙擊package以後,就會出現咱們打好的jar包,而後選擇下面這個就能夠運行了
運行咱們的代碼一共能夠有四個命令,兩種是打包的時候選擇了咱們的main程序類的,兩種是咱們打包時候沒有選擇main程序類的
其中第一種和第三種,都是選定了咱們的main程序類
第二種和第四種都是沒有選定咱們的main程序類
四種運行方式均可以用於運行咱們的jar包
第一種運行方式:咱們打包的時候指定了對應的main方法所在的程序類
scala scaladay01-1.0-SNAPSHOT-jar-with-dependencies.jar
第二種運行方式:無論打包的時候有沒有指定main方法所在的程序類,均可以運行
scala -cp scaladay01-1.0-SNAPSHOT-jar-with-dependencies.jar cn.itcast.scala.demo1.ScalaFirst
第三種方式:咱們打包的時候指定了對應的main方法所在的程序類
java -jar scaladay01-1.0-SNAPSHOT-jar-with-dependencies.jar
第四種方式:無論打包的時候有沒有指定main方法所在的程序類,均可以運行
java -cp scaladay01-1.0-SNAPSHOT-jar-with-dependencies.jar cn.itcast.scala.demo1.ScalaFirst
咱們能夠看到咱們的scala代碼最終也編譯成了class文件
scala當中的變量申明可使用兩種方式,第一種使用val來申明變量。第二種使用var來申明變量。
申明變量語法
val/var 變量名 [:變量類型] = 變量值
其中val定義的變量是不可變的。相似於java當中使用final來進行修飾
REPL自動分配變量名
若是咱們在REPL當中沒有定義變量名,那麼咱們的變量名系統會自動給定
注意:scala當中的變量類型能夠不用指定,系統會自動推斷。爲了減小可變性引發的bug,scala當中推薦儘可能使用不可變類型來申明變量。var和val申明變量的時候,變量都必須初始化
定義變量時用 {} 包含一系列表達式,其中塊的最後一個表達式的值就是塊的值。
var hello = {
println("world")
val d = 20
val c = 10
d-c
}
塊表達式最後的結果,就是咱們變量的值
Scala和Java同樣,有7種數值類型Byte、Char、Short、Int、Long、Float、Double類型和1個Boolean類型。
Boolean |
true 或者 false |
Byte |
8位, 有符號 |
Short |
16位, 有符號 |
Int |
32位, 有符號 |
Long |
64位, 有符號 |
Char |
16位, 無符號 |
Float |
32位, 單精度浮點數 |
Double |
64位, 雙精度浮點數 |
String |
其實就是由Char數組組成 |
與java當中不一樣,scala當中並不區分基本數據類型和引用數據類型,全部的這些類型所有都是對象,能夠調用相對應的方法。在scala當中,String直接引用的是java.lang.String這個java當中的類型。因爲String在須要時能隱式轉換爲StringOps,所以不須要任何額外的轉換,String就可使用這些方法。
每一種數據類型都有對應的Rich* 類型,如RichInt、RichChar等,爲基本類型提供了更多的有用操做。
Scala中,全部的值都是類對象,而全部的類,包括值類型,都最終繼承自一個統一的根類型Any。統一類型,是Scala的又一大特色。更特別的是,Scala中還定義了幾個底層類(Bottom Class),好比Null和Nothing。
1) Null是全部引用類型的子類型,而Nothing是全部類型的子類型。Null類只有一個實例對象,null,相似於Java中的null引用。null能夠賦值給任意引用類型,可是不能賦值給值類型。
2) Nothing,能夠做爲沒有正常返回值的方法的返回類型,很是直觀的告訴你這個方法不會正常返回,並且因爲Nothing是其餘任意類型的子類,他還能跟要求返回值的方法兼容。
3) Unit類型用來標識過程,也就是沒有明確返回值的函數。 因而可知,Unit相似於Java裏的void。Unit只有一個實例,(),這個實例也沒有實質的意義。
+-*/%能夠完成和Java中相同的工做,可是有一點區別,他們都是方法。你幾乎能夠用任何符號來爲方法命名。
舉例:
scala> 1 + 2 等同於: scala> 1.+(2) |
注意:Scala中沒有++、--操做符,須要經過+=、-=來實現一樣的效果。
scala中沒有三目運算符,由於根本不須要。scala中if else表達式是有返回值的,若是if或者else返回的類型不同,就返回Any類型(全部類型的公共超類型)。
例如:if else返回類型同樣
val a = 20
val b = if(a >10){
15
}else{
35
}
例如:if else返回類型不同
val c = 50
val d = if(c > 20){
println("返回一個字符串")
"ABC"
}else{
println("helloworld")
}
若是缺乏一個判斷,什麼都沒有返回,可是Scala認爲任何表達式都會有值,對於空值,使用Unit類,寫作(),叫作無用佔位符,至關於java中的void。
注意:行尾的位置不須要分號,只要可以從上下文判斷出語句的終止便可。可是若是在單行中寫多個語句,則須要分號分割。在Scala中,{}快包含一系列表達式,其結果也是一個表達式。塊中最後一個表達式的值就是塊的值。
scala提供了相似於java的while和do循環,可是while語句的自己是沒有任何返回值類型的,也就是while語句最終的返回結果是Unit類型的()。
var e = 1;
val f = while(e <= 10){
e +=1
}
println(e)
println(f)
scala當中while循環的contine和break:注意:scala當中並無提供相似於java的continue和break操做,若是須要終止循環,咱們能夠有如下幾種方式
一、 使用Boolean標識來進行終端
二、 使用嵌套函數,從函數中直接return
三、 使用Breaks對象當中的break方法
var g = 10
val loop = new Breaks
loop.breakable{
val h = while(g <=20){
g +=1
if(g == 15){
loop.break()
}
}
println(h)
}
println(g+"=============")
scala當中,爲for循環這一經常使用的循環結構提供了不少的特性,這些特性被稱之爲for推導式或者for表達式
示例一:使用to實現左右兩邊均爲閉合的訪問
for(i <- 1 to 3; j <- 1 to 5){
println( i *j +" result result result result")
}
示例二:使用util實現左右兩邊分別爲前閉後開的訪問
for(i <- 1 until 5 ;j <- 2 until 5){
println(i * j )
}
示例三:引入保護式(也稱條件判斷式)。咱們能夠在for循環條件裏面加入判斷表達式,若是知足則進入for循環,若是不知足則不進入for循環。相似於java當中的continue的功能相似
for(i <- 1 to 5 if i!=2){
println(i)
}
示例四:引入變量
for(i <- 1 to 3 ;j = 4-i){
println(i * j)
}
示例五:將遍歷過程當中處理的結果返回到一個變量,使用yield關鍵字進行接收
val for5 = for(i <- 1 to 10) yield i
println(for5+"for5")
示例六:使用大括號代替小括號
for{
i <- 1 to 5
j = 5-i
}
println( i* j +"myij")
在scala中,通常狀況下咱們不會刻意的去區分函數與方法的區別,可是他們確實是不一樣的東西。後面咱們再詳細探討。首先咱們要學會使用scala來調用函數與方法。
1) 調用函數,求方根
scala> import scala.math._ scala> sqrt(100) |
2) 調用方法,靜態方法(scala中沒有靜態方法這個概念,須要經過伴生類對象來實現)
生成一個隨機的素數
scala> BigInt.probablePrime(16, scala.util.Random) |
3) 調用方法,非靜態方法,使用對象調用
scala> "HelloWorld".distinct |
4) apply與update方法
apply方法是調用時能夠省略方法名的方法。用於構造和獲取元素:
"Hello"(4) 等同於 "Hello".apply(4) Array(1,2,3) 等同於 Array.apply(1,2,3) 如: println("Hello"(4)) println("Hello".apply(4)) |
在StringOps中你會發現一個 def apply(n: Int): Char方法定義。update方法也是調用時能夠省略方法名的方法,用於元素的更新:
arr(4) = 5 等同於 arr.update(4,5) 如: val arr1 = new Array[Int](5) arr1(1) = 2 arr1.update(1, 2) println(arr1.mkString(",")) |
在scala當中,函數與方法是兩個不一樣的概念,函數是scala當中的一等公民,scala是一門函數式的編程語言,同時兼顧了面嚮對象語言的特性
scala定義方法的標準格式爲
def 方法名(參數名1: 參數類型1, 參數名2: 參數類型2) : 返回類型 = {方法體} |
示例一:定義一個最標準的方法,且定義方法的返回值類型爲Int類型
def hello(first:String,second:Int) :Int = {
second
}
示例二:定義一個方法,且不定義返回值
注意:若是定義的方法沒有返回值,那麼方法的返回值會作自動推斷。根據咱們方法的最後一個返回類型來推斷咱們的方法返回類型
def hello2(first:Int , second:String) ={
//println(first)
//20
}
val hello2Result = hello2(20,"abc")
println( hello2Result)
示例三:定義一個方法,不定義返回值,能夠經過自動推斷,返回不一樣類型的值
def hello3(first:Int,second:String) ={
if(first > 10){
first
}else{
second
}
}
val hello3Result = hello3(5,"helloworld")
println(hello3Result)
示例四:定義一個方法,參數給定默認值,若是不傳入參數,就使用默認值來代替
def hello4(first:Int = 10,second:String)={
println(first+"\t"+ second)
}
//注意咱們在調用方法的時候咱們能夠經過參數名來指定咱們的參數的值
hello4(second="helloworld")
示例五:變長參數,方法的參數個數不定的,相似於java當中的方法的...可變參數
def hello5(first:Int*)={
var result = 0;
for(arg <- first){
result += arg
}
println(result)
}
hello5(10,20,30)
hello5(10,50)
示例六:遞歸函數。咱們能夠定義一個方法,使得方法本身調用本身,造成一個遞歸函數,可是方法的返回值類型必須顯示的手動指定
def hello6(first:Int):Int={
if(first <= 1){
1
}else{
first * hello6(first -1)
}
}
val hello6Result = hello6(10)
println(hello6Result)
示例七:定義一個方法,沒有顯示的指定返回值,那麼咱們方法當中定義的等號能夠省掉
注意:若是省掉了=號,那麼這個方法強調的就是一個代碼執行的過程
/**
* 定義了一個方法,可是方法的返回值沒有顯示指定,
* 此時咱們就能夠省掉方法定義的=號,若是省掉 = 號,
* 那麼這個方法強調的是一個過程,代碼執行的過程,
* 不會產生任何的返回值
* @param first
*/
def hello7(first:Int){
println(first)
30
}
hello7(20)
示例八:直接經過def定義一個方法
def hello8=10;
val hello8Result = hello8
println(hello8Result)
示例九:若是方法體當中只有一行代碼,咱們也能夠省掉大括號
def hello10(first:Int,second:Int) = first+second
val hello10Result = hello10(10,20)
println(hello10Result)
函數定義的兩種形式
第一種形式:
val 函數名 = (參數名1:參數類型1,參數名2:參數類型2) => {函數體}
第二種形式:
val 函數名 :(參數類型1,參數類型2) => (返回類型) = {
函數體
}
示例一:定義一個標準函數,使用 =>來進行定義
val func1 =(x:Int,y:Int) =>{
x+y
}
func1(2,8)
示例二:定義匿名函數。也就是咱們能夠定義一個沒有名字的函數
定義一個匿名函數以後,這個函數就無法使用了
(x:Int,y:String) =>{x + y}
示例三:函數定義的另一種形式,定義一個函數,參數只有一個且是Int類型,返回值也是Int類型
val func3 :Int => Int = {x => x * x }
val func3Result = func3(10)
示例四:定義一個函數,參數值是兩個,分別是Int和String,返回值是一個元組,分別是String和Int
val func4:(Int,String) =>(String,Int) ={
(x,y) => (y,x)
}
val func4Result = func4(10,"hello")
println(func4Result)
在scala當中,函數與方法是有區別的,函數能夠做爲一個參數,傳入到方法裏面去
咱們能夠定義一個函數,而後再定義一個方法,可是方法的參數是一個函數
val myFunc = (x:Int) =>{
x * x
}
val myFunc2 :(Int) => Int ={
x => x * x
}
def methodFunction(f:Int => Int):Int ={
println(f(100))
f(100)
}
val methodFunctionResult = methodFunction(myFunc)
val methodFunctionResult2 = methodFunction(myFunc2)
println(methodFunctionResult)
println(methodFunctionResult2)
方法能夠自動轉換成函數做爲參數傳遞到方法裏面去
def method2(x:Int) ={ x * x }
def methodFunc2(x:Int => Int):Int ={
x(100)
}
val methodFunc2Result = methodFunc2(method2)
println(methodFunc2Result)
咱們能夠經過 _ 將咱們的一個方法,轉換成函數
def method3(x:Int,y:String ) :Int = {
println(x)
x
}
val methodToFunc = method3 _
println( methodToFunc)
當val被聲明爲lazy時,他的初始化將被推遲,直到咱們首次對此取值,適用於初始化開銷較大的場景。
def init(): String = {
println("init方法執行")
"嘿嘿嘿,喵喵喵~"
}
lazy val msg = init()
println("lazy方法沒有執行")
println(msg)
Scala同時支持可變集合和不可變集合,不可變集合從不可變,能夠安全的併發訪問。
兩個主要的包:
不可變集合:scala.collection.immutable
可變集合: scala.collection.mutable
Scala優先採用不可變集合,對於幾乎全部的集合類,Scala都同時提供了可變和不可變的版本。
不可變集合繼承層次:
可變集合繼承層次:
咱們能夠定義一個固定長度大小和類型的定長數組
//定義一個數組長度爲10,類型爲Int的固定大小數組
val array = new Array[Int](10)
array(1) = 10
array(2) = 20
//訪問數組當中的某一個下標元素值
println(array(1))
//直接使用apply方法進行生成一個數組
val array2 = Array(1,2,3)
//訪問數組的元素
println(array2(2))
咱們也能夠經過ArrayBuffer來定義一個變長數組
val array3 = new ArrayBuffer[Int]()
array3.append(20)
val array4 = ArrayBuffer[String]()
array4.append("helloworld")
定長數組轉換成變長數組
//定長數組轉換成變長數組
val toBuffer = array.toBuffer
toBuffer.append(50)
變長數組轉換爲定長數組
//變長數組準換成定長數組
val toArray = array3.toArray
咱們能夠經過Array的ofDim方法來定義一個多維的數組,多少行,多少列,都是咱們本身定義說了算
val dim = Array.ofDim[Double](3,4)
dim(1)(1) = 11.11
println(dim.mkString(","))
val array5 = ArrayBuffer(1,2,3,4,5,6)
for(x <- array5){
println(x )
}
val array6 = Array(1,2,3,4,5,6)
//求和
array6.sum
//求最大值
array6.max
//排序
array6.sorted
在scala當中提供元組tuple的數據類型,能夠理解tuple爲一個容器,能夠存放各類不一樣的數據類型的數據,例如一個Tuple當中既能夠存放String類型數據,同時也能夠存放Int類型的數據
注意:注意元組一旦建立以後,就是不可變的,也就是說元組當中沒有添加和刪除元素這一說
建立元組,直接使用小括號,小括號當中存放咱們元組當中各類類型的元素便可
val tuple1 = ("hello",1,5.0f)
println(tuple1)
訪問元組當中的數據直接使用_加角標便可,可是要注意,元組當中的數據角標是從1開始的
val tuple1 = ("hello",1,5.0f)
println(tuple1)
val tuple1Result = tuple1._1
println(tuple1Result)
val tuple1 = ("hello",1,5.0f)
println(tuple1)
val tuple1Result = tuple1._1
println(tuple1Result)
//第一種方式遍歷元組
for(x <- tuple1.productIterator){
println(x)
}
//第二種方式遍歷元組
tuple1.productIterator.foreach( x => println(x))
scala當中的Map集合與java當中的Map相似,也是key,value對形式的
val map1 = Map("hello" ->"world","name" -> "zhangsan","age" -> 18)
val map2 = scala.collection.mutable.Map("hello" ->"world","name" -> "zhangsan","age" -> 18)
//可變map添加元素
map2.+=("address" ->"地球")
println(map2)
//可變map刪除元素.注意,刪除元素是返回一個刪除元素以後的map,原來的map並無改變
val map3 = map2.-("address")
println(map2)
println(map3)
//或者使用覆蓋key的方式來更細元素
map2 += ("address" -> "北京")
println(map2)
//或者使用 + 來進行更新元素
//注意,map當中沒有phonNo這個key,則不能更細
map2 +("address" ->"上海","phonNo" -> "13688886666")
println(map2)
//經過key來進行取值
map2.get("address")
//經過key來進行取值,若是沒有這個key,就用後面給定的默認值
map2.getOrElse("address","非洲")
//經過key來進行取值,真的沒有這個key,那麼就用後面給定的默認值
map2.getOrElse("phoNo","13133335555")
//遍歷key與value
for((k,v) <- map2){
println(k)
println(v)
}
//遍歷獲取全部的key
for(k <- map2.keys) {
println(k)
}
//遍歷獲取全部的value
for(v <- map2.values) {
println(v)
}
//打印key,value對
for(kv <- map2){
println(kv)
}
//將對偶的元組轉變爲map
val arrayMap = Array(("name","zhangsan"),("age",28))
val toMap = arrayMap.toMap
println(toMap)
scala當中也提供有與java相似的List集合操做
注意:列表當中的元素類型能夠是不一樣的,這一點與咱們元組相似,可是列表當中的元素是能夠刪減的
val list1 = List("hello",20,5.0f)
println(list1)
//訪問列表當中的元素
val list1Result = list1(0)
println(list1Result)
咱們能夠從列表頭部或者尾部添加元素
val list2 = list1:+50
val list3 = 100+:list1
println(list2)
println(list3)
Nil是一個空的List,定義爲List[Nothing]
//尾部添加了Nil,那麼就會出現List集合裏面裝List集合的現象
val list4 = 1::2 ::3 :: list1 ::Nil
println(list4)
//尾部沒有添加Nil的值,那麼全部的元素都壓平到一個集合裏面去了
val list5 = 1::2::3::list1
println(list5)
val list6 = new ListBuffer[String]
list6.append("hello")
list6.append("world")
println(list6.mkString(","))
val list7 = list6.toList
println(list7)
Scala是函數式風格與面向對象共存的編程語言,方法不該該有反作用是函數風格編程的一個重要的理念。方法惟一的效果應該是計算並返回值,用這種方式工做的好處就是方法之間不多糾纏在一塊兒,所以就更加可靠和可重用。另外一個好處(靜態類型語言)是傳入傳出方法的全部東西都被類型檢查器檢查,所以邏輯錯誤會更有可能把本身表現爲類型錯誤。把這個函數式編程的哲學應用到對象世界裏覺得着使對象不可變。
前面一章介紹的Array數組是一個全部對象都共享相同類型的可變序列。比方說Array[String]僅包含String。儘管實例化以後你沒法改變Array的長度。所以,Array是可變的對象。
說到共享相同類型的不可變對象類型,Scala的List類纔是。和數組同樣,List[String]包含的僅僅是String。Scala的List不一樣於Java的java.util.List,老是不可變的(Java的List是可變)。更準確的說法,Scala的List是設計給函數式風格的編程用的。
(1)List類型定義以及List的特色:
//字符串類型List
scala> val fruit=
List(
"Apple",
"Banana",
"Orange")
fruit:
List[String] =
List(Apple, Banana, Orange)
//前一個語句與下面語句等同
scala> val fruit=
List.apply(
"Apple",
"Banana",
"Orange")
fruit:
List[String] =
List(Apple, Banana, Orange)
//數值類型List
scala> val nums=
List(
1,
2,
3,
4,
5)
nums:
List[Int] =
List(
1,
2,
3,
4,
5)
//多重List,List的子元素爲List
scala> val
list =
List(
List(
1,
2,
3),
List(
"adfa",
"asdfa",
"asdf"))
list:
List[
List[Any]] =
List(
List(
1,
2,
3),
List(adfa, asdfa, asdf))
//遍歷List
scala>
for(i <-
list; from=i; j<-from)println(j)
1
2
3
adfa
asdfa
asdf
(2)List與Array的區別:
一、List一旦建立,已有元素的值不能改變,可使用添加元素或刪除元素生成一個新的集合返回。
如前面的nums,改變其值的話,編譯器就會報錯。而Array就能夠成功
scala>nums(
3)=
4
<
console>:
10: error: value update
is
not a member
of List[Int]
nums(
3)=
4
^
二、List具備遞歸結構(Recursive Structure),例如鏈表結構
List類型和睦他類型集合同樣,它具備協變性(Covariant),即對於類型S和T,若是S是T的子類型,則List[S]也是List[T]的子類型。
例如:
scala>
var listStr:
List[
Object] =
List(
"This",
"Is",
"Covariant",
"Example")
listStr:
List[
Object] =
List(This, Is, Covariant, Example)
//空的List,其類行爲Nothing,Nothing在Scala的繼承層次中的最底層
//,即Nothing是任何Scala其它類型如String,Object等的子類
scala>
var listStr =
List()
listStr:
List[Nothing] =
List()
scala>
var listStr:
List[
String] =
List()
listStr:
List[
String] =
List()
(3)List經常使用構造方法
//一、經常使用::及Nil進行列表構建
scala> val nums =
1 :: (
2:: (
3:: (
4 :: Nil)))
nums:
List[Int] =
List(
1,
2,
3,
4)
//因爲::操做符的優先級是從右向左的,所以上一條語句等同於下面這條語句
scala> val nums =
1::
2::
3::
4::Nil
nums:
List[Int] =
List(
1,
2,
3,
4)
至於
::
操做符的使用將在下面介紹
(4)List經常使用操做
//判斷是否爲空
scala> nums.isEmpty
res5: Boolean =
false
//取第一個元素
scala> nums.head
res6: Int =
1
//取列表第二個元素
scala>nums.tail.head
res7: Int =
2
//取第三個元素
scala>nums.tail.tail.head
res8: Int =
3
//插入操做
//在第二個位置插入一個元素
scala>nums.head::(
3::nums.tail)
res11: List[Int] = List(
1,
3,
2,
3,
4)
scala> nums.head::(nums.tail.head::(
4::nums.tail.tail))
res12: List[Int] = List(
1,
2,
4,
3,
4)
//插入排序算法實現
def isort(xs: List[Int]):List[Int] = {
if(xs.isEmpty)
Nil
else insert(xs.head, issort(xs.tail))
}
def insert(x:Int, xs:List[Int]):List[Int] = {
if(xs.isEmpty || x <= xs.head) x::xs
else xs.head :: insert(x, xs.tail)
}
//鏈接操做
scala>List(
1,
2,
3):::List(
4,
5,
6)
res13: List[Int] = List(
1,
2,
3,
4,
5,
6)
//去除最後一個元素外的元素,返回的是列表
scala> nums.init
res13: List[Int] = List(
1,
2,
3)
//取出列表最後一個元素
scala>nums.last
res14: Int =
4
//列表元素倒置
scala> nums.reverse
res15: List[Int] = List(
4,
3,
2,
1)
//一些好玩的方法調用
scala> nums.reverse.reverse == nums
//丟棄前面n個元素
scala>nums drop
3
res16: List[Int] = List(
4)
//獲取前面n個元素
scala>nums take
1
res17: List[Int] = List[
1]
//將列表進行分割
scala> nums.splitAt(
2)
res18: (List[Int], List[Int]) = (List(
1,
2),List(
3,
4))
//前一個操做與下列語句等同
scala> (nums.take(
2),nums.drop(
2))
res19: (List[Int], List[Int]) = (List(
1,
2),List(
3,
4))
//Zip操做
scala> val nums=List(
1,
2,
3,
4)
nums: List[Int] = List(
1,
2,
3,
4)
scala> val chars=List(
'1',
'2',
'3',
'4')
chars: List[Char] = List(
1,
2,
3,
4)
//返回的是List類型的元組(Tuple),返回的元素個數與最小的List集合的元素個數同樣
scala> nums zip chars
res20: List[(Int, Char)] = List((
1,
1), (
2,
2), (
3,
3), (
4,
4))
//List toString方法
scala> nums.toString
res21: String = List(
1,
2,
3,
4)
//List mkString方法
scala> nums.mkString
res22: String =
1234
//轉換成數組
scala> nums.toArray
res23: Array[Int] = Array(
1,
2,
3,
4)
(5)List伴生對象方法
//apply方法
scala>
List.apply(
1,
2,
3)
res24:
List[Int] =
List(
1,
2,
3)
//range方法,構建某一值範圍內的List
scala>
List.range(
2,
6)
res25:
List[Int] =
List(
2,
3,
4,
5)
//步長爲2
scala>
List.range(
2,
6,
2)
res26:
List[Int] =
List(
2,
4)
//步長爲-1
scala>
List.range(
2,
6,
-1)
res27:
List[Int] =
List()
scala>
List.range(
6,
2 ,
-1)
res28:
List[Int] =
List(
6,
5,
4,
3)
//構建相同元素的List
scala>
List.make(
5,
"hey")
res29:
List[
String] =
List(hey, hey, hey, hey, hey)
//unzip方法
scala>
List.unzip(res20)
res30: (
List[Int],
List[Char]) = (
List(
1,
2,
3,
4),
List(
1,
2,
3,
4))
//list.flatten,將列表平滑成第一個無素
scala> val xss =
|
List(
List(
'a',
'b'),
List(
'c'),
List(
'd',
'e'))
xss:
List[
List[Char]] =
List(
List(a, b),
List(c),
List(d, e))
scala> xss.flatten
res31:
List[Char] =
List(a, b, c, d, e)
//列表鏈接
scala>
List.concat(
List(
'a',
'b'),
List(
'c'))
res32:
List[Char] =
List(a
, b, c)
(6)::和:::操做符介紹
List中經常使用'::',發音爲"cons"。Cons把一個新元素組合到已有元素的最前端,而後返回結果List。
scala> val twoThree = List(2, 3)
scala> val oneTwoThree = 1 :: twoThree
scala> oneTwoThree
oneTwoThree: List[Int] = List(1, 2, 3)
上面表達式"1::twoThree"中,::是右操做數,列表twoThree的方法。可能會有疑惑。表達式怎麼是右邊參數的方法,這是Scala語言的一個例外的狀況:若是一個方法操做符標註,如a * b,那麼方法被左操做數調用,就像a.* (b)--除非方法名以冒號結尾。這種狀況下,方法被右操做數調用。
List有個方法叫":::",用於實現疊加兩個列表。
scala> val one =
List(
'A',
'B')
val one =
List(
'A',
'B')
scala> val two =
List(
'C',
'D')
scala> one:::two
res1:
List[Char] =
List(A, B, C, D)
集是不重複元素的結合。集不保留順序,默認是以哈希集實現。
若是想要按照已排序的順序來訪問集中的元素,可使用SortedSet(已排序數據集),已排序的數據集是用紅黑樹實現的。
默認狀況下,Scala 使用的是不可變集合,若是你想使用可變集合,須要引用 scala.collection.mutable.Set 包。
val set1 =Set("1","1","2","3")
println(set1.mkString(","))
若是咱們引入的集合的包是可變的,那麼咱們建立的集合就是可變的
import scala.collection.mutable.Set
val set2 = Set(1, 2, 3)
set2.add(4)
set2 += 5
//使用.這個方法添加元素,會返回一個新的集合
val set3 = set2.+(6)
println(set2.mkString(","))
println(set3.mkString("\001"))
set3 -= 1
println(set3.mkString("."))
set3.remove(2)
println(set3.mkString("."))
for(x <- set3){
println(x )
}
注意:若是要建立有序的set,那麼須要使用SortedSet。用法與Set相似
更多Set集合操做參見以下:
http://www.runoob.com/scala/scala-sets.html
序號 |
方法 |
描述 |
1 |
def +(elem: A): Set[A] |
爲集合添加新元素,並建立一個新的集合,除非元素已存在 |
2 |
def -(elem: A): Set[A] |
移除集合中的元素,並建立一個新的集合 |
3 |
def contains(elem: A): Boolean |
若是元素在集合中存在,返回 true,不然返回 false。 |
4 |
def &(that: Set[A]): Set[A] |
返回兩個集合的交集 |
5 |
def &~(that: Set[A]): Set[A] |
返回兩個集合的差集 |
6 |
def ++(elems: A): Set[A] |
合併兩個集合 |
7 |
def drop(n: Int): Set[A]] |
返回丟棄前n個元素新集合 |
8 |
def dropRight(n: Int): Set[A] |
返回丟棄最後n個元素新集合 |
9 |
def dropWhile(p: (A) => Boolean): Set[A] |
從左向右丟棄元素,直到條件p不成立 |
10 |
def max: A |
查找最大元素 |
11 |
def min: A |
查找最小元素 |
12 |
def take(n: Int): Set[A] |
返回前 n 個元素 |
咱們可使用map方法,傳入一個函數,而後將這個函數做用在集合當中的每個元素上面
map:將集合中的每個元素映射到某一個函數
val listFunc = List("name","age","zhangsan","lisi")
println(listFunc.map(x => x +"hello"))
println(listFunc.map(_.toUpperCase()))
flatmap:flat即壓扁,壓平,扁平化,效果就是將集合中的每一個元素的子元素映射到某個函數並返回新的集合
val listFunc2 = List("address","phonNo")
println(listFunc2.flatMap( x => x +"WORLD"))
隊列Queue是一個先進先出的結構
//建立可變的隊列
val queue1 = new mutable.Queue[Int]()
println(queue1)
//隊列當中添加元素
queue1 += 1
//隊列當中添加List
queue1 ++=List(2,3,4)
println(queue1)
val dequeue = queue1.dequeue()
println(dequeue)
println(queue1)
//塞入元素到隊列
queue1.enqueue(5,6,7)
println(queue1)
//獲取第一個元素
println(queue1.head)
//獲取最後一個元素
println(queue1.last)
將二元函數引用集合當中的函數
val reduceList = List(1,2,3,4,5)
//1-2-3-4-5 = -13
val reduceLeftList = reduceList.reduceLeft(_ - _)
val reduceLeftList2 = reduceList.reduceLeft((x,y) => x-y)
println(reduceLeftList)
println(reduceLeftList2)
//reduceRight操做
// 4-5 = -1
// 3- (-1) = 4
//2-4 = -2
//1 -(-2) = 3
val reduceRightList = reduceList.reduceRight(_ - _)
println(reduceRightList)
fold函數將上一步返回的值做爲函數的第一個參數繼續傳遞參與運算,直到list中的全部元素被遍歷。能夠把reduceLeft看作簡化版的foldLeft。相關函數:fold,foldLeft,foldRight,能夠參考reduce的相關方法理解。
reduce的本質其實就是fold操做,只不過咱們使用fold操做的時候,須要指定初始值
fold操做
val foldList = List(1,9,2,8)
val foldResult = foldList.fold(10)((x,y) => x+y)
println(foldResult)
foldLeft操做
//50-1-9-2-8 = 30
val foldLeftResult = foldList.foldLeft(50)((x,y) => x-y)
println(foldLeftResult)
對於多個List集合,咱們可使用Zip操做,將多個集合當中的值綁定到一塊兒去
val zipList1 = List("name","age","sex")
val zipList2 = List("zhangsan",28)
val zip = zipList1.zip(zipList2)
val toMap1 = zip.toMap
println(zip)
println(toMap1)
對於集合當中的元素,咱們也可使用迭代器來進行遍歷
val listIterator = List(1,2,"zhangsan")
val iterator = listIterator.iterator
while(iterator.hasNext){
println(iterator.next())
}
scala當中爲了解決多線程併發的問題,提供對應的線程安全的集合
https://www.scala-lang.org/api/2.11.8/#package
SynchronizedBuffer
SynchronizedMap
SynchronizedPriorityQueue
SynchronizedQueue
SynchronizedSet
大體瞭解便可
1) 若是想在變量名、類名等定義中使用語法關鍵字(保留字),能夠配合反引號反引號:
val `val` = 42 |
2) 這種形式叫中置操做符,A操做符B等同於A.操做符(B)
3) 後置操做符,A操做符等同於A.操做符,若是操做符定義的時候不帶()則調用時不能加括號
4) 前置操做符,+、-、!、~等操做符A等同於A.unary_操做符。
5) 賦值操做符,A操做符=B等同於A=A操做符B
函數能夠做爲一個參數傳入到一個方法當中去
def main(args: Array[String]): Unit = {
val myFu(x:Int) =>{
x * x
}nc1 =
val myArray = Array(1,3,5,7,9).map(myFunc1)
println(myArray.mkString(","))
}
沒有名字的函數便是匿名函數,咱們能夠經過函數表達式來設置匿名函數
def main(args: Array[String]): Unit = {
println((x:Int,y:String) => x + y)
}
一、可以接受函數做爲參數的方法,叫作高階函數
def main(args: Array[String]): Unit = {
val func3:(Int,String) =>(String,Int)={
(x,y)=>(y,x)
}
def myMethod3(hello:(Int,String) => (String,Int)):Int ={
val resultFunc = hello(20,"hello")
resultFunc._2
}
println(myMethod3(func3))
}
二、高階函數一樣能夠返回一個函數類型
def main(args: Array[String]): Unit = {
def myFunc4(x:Int) = (y:String) => y
println(myFunc4(50))
}
def main(args: Array[String]): Unit = {
val array = Array(1,2,3,4,5,6,7,8,9)
//map當中須要傳入一個函數,咱們能夠直接定義一個函數
array.map((x:Int) => x * 2 )
//進一步化簡 參數推斷省去類型信息
array.map((x) => x * 2 )
//進一步化簡 單個參數能夠省去括號
array.map( x => x * 2 )
//進一步化簡 若是變量只在=>右邊只出現一次,能夠用_來代替
array.map( 2 * _ )
}
柯里化存在的意義是什麼???注意:柯里化是scala當中面向函數式編程致使的一種必然的結果,最終推導而來產生的一種現象
def main(args: Array[String]): Unit = {
//柯里化的定義形式
def kery(x:Int)(y:Int):Int={
x + y
}
println(kery(3)(5))
//柯里化的推導過程,注意方法的返回值不要定義任何的返回值類型
val keryResult = (x:Int) => (y:Int) => {x + y}
def keryMethod(x:Int) ={
(y:Int) => x+ y
}
println(keryMethod(20))
println(keryMethod(20)(10))
//注意,方法當中的函數,調用了方法的參數,就叫作閉包
}
再來看一個案例
/**
* 柯里化的應用,比較數組集合當中兩個對應下標字符串是否相等
* @param args
*/
def main(args: Array[String]): Unit = {
val a = Array("Hello", "World")
val b = Array("hello", "world")
println(a.corresponds(b)(_.equalsIgnoreCase(_)))
}
建立一個scala class來定義咱們的一個類。類當中能夠定義各類屬性或者方法,或者函數均可以
class Person {
//定義一個屬性,叫作name的,使用val不可變量來進行修飾
// 用val修飾的變量是可讀屬性,有getter但沒有setter(至關與Java中用final修飾的變量)
val name:String ="zhangsan"
//定義一個屬性,叫作age的,使用var可變量來進行修飾
//用var修飾的變量都既有getter,又有setter
var age:Int = 28
//類私有字段,只能在類的內部使用或者伴生對象中訪問
private val address:String = "地球上"
//類私有字段,訪問權限更加嚴格的,該字段在當前類中被訪問
//在伴生對象裏面也不能夠訪問
private[this] var pet = "小強"
//在類當中定義了一個方法,
def hello(first:Int,second:String):Int ={
println(first+"\t"+second)
250
}
/**
* 定義了一個函數
*/
val func1 =(x:Int,y:Int) =>{
x+y
}
}
class Person{
|
若是想要使用類的話,那麼REPL就知足不了咱們的要求了,咱們從新建立一個對應的Object的scala文件
object ScalaClass {
def main(args: Array[String]): Unit = {
//建立對象兩種方式。這裏都是使用的無參構造來進行建立對象的
val person = new Person
val person1 = new Person()
//注意,咱們可使用對象的屬性加上_= 給var修飾的屬性進行從新賦值
//其實就是調用set方法,方法名叫作 age_=
person.age_= (50)
//直接調用類的屬性,其實就是調用get方法
println(person.age)
println(person.hello(50,"helloworld"))
val func = person.func1(10,20)
println(func)
println("============")
}
}
對於scala類中的每個屬性,編譯後,會有一個私有的字段和相應的getter、setter方法生成
//getter方法
println(person age)
//setter方法
println(person age_= (18))
//getter方法
println(person.age)
固然了,你也能夠不使用自動生成的方式,本身定義getter和setter方法
class Dog2 { private var _leg = 4 def leg = _leg def leg_=(newLeg: Int) { _leg = newLeg } } |
使用之:
val dog2 = new Dog2 dog2.leg_=(10) println(dog2.leg) |
規範提示:本身手動建立變量的getter和setter方法須要遵循如下原則:
1) 字段屬性名以「_」做爲前綴,如:_leg
2) getter方法定義爲:def leg = _leg
3) setter方法定義時,方法名爲屬性名去掉前綴,並加上後綴,後綴是:「leg_=」,如例子所示
scala當中類的構造器分爲兩種:主構造器和輔助構造器
scala當中規定,全部的輔助構造器,最後都必須調用另一個構造器,另一個構造器能夠是輔助構造器,也能夠是主構造器
//主構造器,直接定義在類上面
class Dog (name:String,age:Int){
//在scala當中,能夠直接將代碼寫在class當中,而在java當中,
//代碼必須包含在方法當中。
//其實在scala當中,雖然你把代碼寫在了Class類當中,通過編譯以後,
//class類的代碼都進入到了主構造器方法當中去了
println(name)
println(age)
var gender:String = "";
def this(name:String,age:Int,gender:String){
//每一個輔助構造器,都必須以其餘輔助構造器,或者主構造器的調用做爲第一句
this(name:String,age:Int)
this.gender = gender
}
var color ="";
/**
* 咱們也能夠經過private來進行修飾咱們的構造器,
* @param name
* @param age
* @param color
* @param gender
*/
private def this(name:String,age:Int,color:String,gender:String){
this(name:String,age:Int)
this.color = color
}
}
在scala當中,沒有相似於像java當中的static修飾的靜態屬性或者靜態方法或者靜態代碼塊之類的,可是咱們能夠經過scala當中的Object來實現相似的功能。能夠理解爲scala當中的Object裏面的屬性或者方法都是靜態的,能夠直接調用
定義一個class類,而後在class類當中定義一個Object的對象。object對象當中的全部屬性或者方法都是靜態的
class Session {
def hello(first:Int):Int={
println(first)
first
}
}
object SessionFactory{
val session = new Session
def getSession():Session ={
session
}
def main(args: Array[String]): Unit = {
for(x <- 1 to 10){
//經過直接調用,產生的對象都是單列的
val session = SessionFactory.getSession()
println(session)
}
}
}
class ClassObject {
val id = 1
private var name = "itcast"
def printName(): Unit ={
//在Dog類中能夠訪問伴生對象Dog的私有屬性
println(ClassObject.CONSTANT + name )
}
}
object ClassObject{
//伴生對象中的私有屬性
private val CONSTANT = "汪汪汪 : "
def main(args: Array[String]) {
val p = new ClassObject
//訪問私有的字段name
p.name = "123"
p.printName()
}
}
class ApplyObjectClass (name:String){
println(name)
}
object ApplyObjectClass{
def apply(name:String): ApplyObjectClass = {
new ApplyObjectClass(name)
}
def main(args: Array[String]): Unit = {
//調用的apply方法來建立對象
val applyObjectClass = ApplyObjectClass("lisi")
//調用的是new Class來建立對象
val applyObjectClass2 =new ApplyObjectClass("wangwu")
}
}
//1.在object中定義main方法
object Main_Demo1 {
def main(args: Array[String]) {
if(args.length > 0){
println("Hello, " + args(0))
}else{
println("Hello World1!")
}
}
}
//2.使用繼承App Trait ,將須要寫在 main 方法中運行的代碼
// 直接做爲 object 的 constructor 代碼便可,
// 並且還可使用 args 接收傳入的參數。
object Main_Demo2 extends App{
if(args.length > 0){
println("Hello, " + args(0))
}else{
println("Hello World2!")
}
}
Scala中沒有枚舉類型,可是咱們能夠退經過定義一個擴展Enumeration類的對象,並以value調用初始化枚舉中的全部可能值:
|
類內部 |
本包 |
子類 |
外部包 |
|
|
|
|
|
public |
√ |
√ |
√ |
√ |
|
|
|
|
|
protected |
√ |
√ |
√ |
× |
|
|
|
|
|
default |
√ |
√ |
× |
× |
|
|
|
|
|
private |
√ |
× |
× |
× |
|
|
|
|
|
package cn.itcast.extends_demo
class Person {
val name="super"
def getName=this.name
}
class Student extends Person{
//繼承加上關鍵字
override
val name="sub"
//子類能夠定義本身的field和method
val score="A"
def getScore=this.score
}
class Person1 {
private val name = "leo"
val age=50
def getName = this.name
}
class Student1 extends Person1{
private val score = "A"
//子類能夠覆蓋父類的 val field,使用override關鍵字
override
val age=30
def getScore = this.score
//覆蓋父類非抽象方法,必需要使用 override 關鍵字
//同時調用父類的方法,使用super關鍵字
override def getName = "your name is " + super.getName
}
若是實例化了子類的對象,可是將其賦予了父類類型的變量,在後續的過程當中,又須要將父類類型的變量轉換爲子類類型的變量,應該如何作?
Scala |
Java |
obj.isInstanceOf[C] |
obj instanceof C |
obj.asInstanceOf[C] |
(C)obj |
classOf[C] |
C.class |
package cn.itcast.extends_demo
class Person3 {}
class Student3 extends Person3
object Student3{
def main (args: Array[String] ) {
val p: Person3 = new Student3
var s: Student3 = null
//若是對象是 null,則 isInstanceOf 必定返回 false
println (s.isInstanceOf[Student3])
// 判斷 p 是否爲 Student3 對象的實例
if (p.isInstanceOf[Student3] ) {
//把 p 轉換成 Student3 對象的實例
s = p.asInstanceOf[Student3]
}
println (s.isInstanceOf[Student3] )
}
}
package cn.itcast.extends_demo
class Person4 {}
class Student4 extends Person4
object Student4{
def main(args: Array[String]) {
val p:Person4=new Student4
//判斷p是否爲Person4類的實例
println(p.isInstanceOf[Person4])//true
//判斷p的類型是否爲Person4類
println(p.getClass == classOf[Person4])//false
//判斷p的類型是否爲Student4類
println(p.getClass == classOf[Student4])//true
}
}
package cn.itcast.extends_demo
class Person5 {}
class Student5 extends Person5
object Student5{
def main(args: Array[String]) {
val p:Person5=new Student5
p match {
// 匹配是否爲Person類或其子類對象
case per:Person5 => println("This is a Person5's Object!")
// 匹配全部剩餘狀況
case _ =>println("Unknown type!")
}
}
}
package cn.itcast.extends_demo
class Person6{
protected var name:String="tom"
protected[this] var hobby:String ="game"
protecteddef sayBye=println("再見...")
}
class Student6 extends Person6{
//父類使用protected 關鍵字來修飾 field能夠直接訪問
def sayHello =println("Hello "+name)
//父類使用protected 關鍵字來修飾method能夠直接訪問
def sayByeBye=sayBye
def makeFriends(s:Student6)={
println("My hobby is "+hobby+", your hobby is UnKnown")
}
}
object Student6{
def main(args: Array[String]) {
val s:Student6=new Student6
s.sayHello
s.makeFriends(s)
s.sayByeBye
}
}
package cn.itcast.extends_demo
class Person7(val name:String,val age:Int){
var score :Double=0.0
var address:String="beijing"
def this(name:String,score:Double)={
//每一個輔助constructor的第一行都必須調用其餘輔助constructor或者主constructor代碼
//主constructor代碼
this(name,30)
this.score=score
}
//其餘輔助constructor
def this(name:String,address:String)={
this(name,100.0)
this.address=address
}
}
class Student7(name:String,score:Double) extends Person7(name,score)
package cn.itcast.extends_demo
abstract class Person9(val name:String) {
//必須指出返回類型,否則默認返回爲Unit
def sayHello:String
def sayBye:String
}
classStudent9(name:String) extends Person9(name){
//必須指出返回類型,否則默認
def sayHello: String = "Hello,"+name
def sayBye: String ="Bye,"+name
}
object Student9{
def main(args: Array[String]) {
val s = new Student9("tom")
println(s.sayHello)
println(s.sayBye)
}
}
package cn.itcast.extends_demo
abstract class Person10 (val name:String){
//抽象fields
val age:Int
}
class Student10(name: String) extends Person10(name) {
val age: Int = 50
}
package cn.itcast.triat
trait HelloTrait {
def sayHello(): Unit
}
trait MakeFriendsTrait {
def makeFriends(c: Children): Unit
}
//多重繼承 trait
class Children(val name: String) extends HelloTrait with MakeFriendsTrait with Cloneable with Serializable{
def sayHello() =println("Hello, " + this.name)
def makeFriends(c: Children) = println("Hello, my name is " + this.name + ", your name is " + c.name)
}
object Children{
def main(args: Array[String]) {
val c1=new Children("tom")
val c2=new Children("jim")
c1.sayHello()//Hello, tom
c1.makeFriends(c2)//Hello, my name is tom, your name is jim
}
}
package cn.itcast.triat
/**
* 好比 trait 中能夠包含不少子類都通用的方法,例如打印日誌或其餘工具方法等等。
* spark就使用trait定義了通用的日誌打印方法;
*/
trait Logger {
def log(message: String): Unit = println(message)
}
class PersonForLog(val name: String) extends Logger {
def makeFriends(other: PersonForLog) = {
println("Hello, " + other.name + "! My name is " + this.name + ", I miss you!!")
this.log("makeFriends method is invoked with parameter PersonForLog[name = " + other.name + "]")
}
}
object PersonForLog{
def main(args: Array[String]) {
val p1=new PersonForLog("jack")
val p2=new PersonForLog("rose")
p1.makeFriends(p2)
//Hello, rose! My name is jack, I miss you!!
//makeFriens method is invoked with parameter PersonForLog[name = rose]
}
}
package cn.itcast.triat
trait PersonForField {
val age:Int=50
}
//繼承 trait 獲取的field直接被添加到子類中
class StudentForField(val name: String) extends PersonForField {
def sayHello = println("Hi, I'm " + this.name + ", my age is "+ age)
}
object StudentForField{
def main(args: Array[String]) {
val s=new StudentForField("tom")
s.sayHello
}
}
package cn.itcast.triat
trait SayHelloTrait {
val msg:String
def sayHello(name: String) = println(msg + ", " + name)
}
class PersonForAbstractField(val name: String) extends SayHelloTrait {
//必須覆蓋抽象 field
val msg = "Hello"
def makeFriends(other: PersonForAbstractField) = {
this.sayHello(other.name)
println("I'm " + this.name + ", I want to make friends with you!!")
}
}
object PersonForAbstractField{
def main(args: Array[String]) {
val p1=new PersonForAbstractField("Tom")
val p2=new PersonForAbstractField("Rose")
p1.makeFriends(p2)
}
}
package cn.itcast.triat
trait LoggedTrait {
// 該方法爲實現的具體方法
def log(msg: String) = {}
}
trait MyLogger extends LoggedTrait{
// 覆蓋 log() 方法
override def log(msg: String) = println("log: " + msg)
}
class PersonForMixTraitMethod(val name: String) extends LoggedTrait {
def sayHello = {
println("Hi, I'm " + this.name)
log("sayHello method is invoked!")
}
}
object PersonForMixTraitMethod{
def main(args: Array[String]) {
val tom= new PersonForMixTraitMethod("Tom").sayHello //結果爲:Hi, I'm Tom
// 使用 with 關鍵字,指定混入MyLogger trait
val rose = new PersonForMixTraitMethod("Rose") with MyLogger
rose.sayHello
// 結果爲: Hi, I'm Rose
// 結果爲: log: sayHello method is invoked!
}
}
package cn.itcast.triat
trait HandlerTrait {
def handle(data: String) = {println("last one")}
}
trait DataValidHandlerTrait extends HandlerTrait {
override def handle(data: String) = {
println("check data: " + data)
super.handle(data)
}
}
trait SignatureValidHandlerTrait extends HandlerTrait {
override def handle(data: String) = {
println("check signature: " + data)
super.handle(data)
}
}
class PersonForRespLine(val name: String) extends SignatureValidHandlerTrait with DataValidHandlerTrait {
def sayHello = {
println("Hello, " + this.name)
this.handle(this.name)
}
}
object PersonForRespLine{
def main(args: Array[String]) {
val p=new PersonForRespLine("tom")
p.sayHello
//執行結果:
// Hello, tom
// check data: tom
// check signature: tom
// last one
}
}
package cn.itcast.triat
trait ValidTrait {
//抽象方法
def getName: String
//具體方法,具體方法的返回值依賴於抽象方法
def valid: Boolean = {"Tom".equals(this.getName)
}
}
class PersonForValid(val name: String) extends ValidTrait {
def getName: String = this.name
}
object PersonForValid{
def main(args: Array[String]): Unit = {
val person = new PersonForValid("Rose")
println(person.valid)
}
}
package cn.itcast.triat
class Person_One {
println("Person's constructor!")
}
trait Logger_One {
println("Logger's constructor!")
}
trait MyLogger_One extends Logger_One {
println("MyLogger's constructor!")
}
trait TimeLogger_One extends Logger_One {
println("TimeLogger's contructor!")
}
class Student_One extends Person_One with MyLogger_One with TimeLogger_One {
println("Student's constructor!")
}
object exe_one {
def main(args: Array[String]): Unit = {
val student = new Student_One
//執行結果爲:
// Person's constructor!
// Logger's constructor!
// MyLogger's constructor!
// TimeLogger's contructor!
// Student's constructor!
}
}
package cn.itcast.triat
class MyUtil {
def printMsg(msg: String) = println(msg)
}
trait Logger_Two extends MyUtil {
def log(msg: String) = this.printMsg("log: " + msg)
}
class Person_Three(val name: String) extends Logger_Two {
def sayHello {
this.log("Hi, I'm " + this.name)
this.printMsg("Hello, I'm " + this.name)
}
}
object Person_Three{
def main(args: Array[String]) {
val p=new Person_Three("Tom")
p.sayHello
//執行結果:
// log: Hi, I'm Tom
// Hello, I'm Tom
}
}
Scala有一個十分強大的模式匹配機制,能夠應用到不少場合:如switch語句、類型檢查等。而且Scala還提供了樣例類,對模式匹配進行了優化,能夠快速進行匹配。
def main(args: Array[String]): Unit = {
val charStr = '6'
charStr match {
case '+' => println("匹配上了加號")
case '-' => println("匹配上了減號")
case '*' => println("匹配上了乘號")
case '/' => println("匹配上了除號")
//注意。全部的模式匹配都必須最終匹配上一個值,若是沒有匹配上任何值,就會報錯
// case _ => println("都沒有匹配上,我是默認值")
}
}
def main(args: Array[String]): Unit = {
val arr = Array("hadoop", "zookeeper", "spark")
val name = arr(Random.nextInt(arr.length))
name match {
case "hadoop" => println("大數據分佈式存儲和計算框架...")
case "zookeeper" => println("大數據分佈式協調服務框架...")
case "spark" => println("大數據分佈式內存計算框架...")
case _ => println("我不認識你...")
}
}
模式匹配當中,咱們也能夠經過條件進行判斷
def main(args: Array[String]): Unit = {
var ch = "500"
var sign = 0
ch match {
case "+" => sign = 1
case "-" => sign = 2
case _ if ch.equals("500") => sign = 3
case _ => sign = 4
}
println(ch + " " + sign)
}
注意在map當中會存在泛型擦除的狀況。注意在進行非數組的類型匹配的時候,類型都會進行擦除
def main(args: Array[String]): Unit = {
//注意泛型擦除,在模式匹配當中的類型匹配中,除了Array類型覺得,全部的其餘的數據類型都會被擦除掉
val a = 3
val obj = if(a == 1) 1
else if(a == 2) "2"
else if(a == 3) BigInt(3)
else if(a == 4) Map("aa" -> 1)
else if(a == 5) Map(1 -> "aa")
else if(a == 6) Array(1, 2, 3)
else if(a == 7) Array("aa", 1)
else if(a == 8) Array("aa")
val r1 = obj match {
case x: Int => x
case s: String => s.toInt
case BigInt => -1 //不能這麼匹配
case _: BigInt => Int.MaxValue
case m: Map[String, Int] => "Map[String, Int]類型的Map集合"
case m: Map[_, _] => "Map集合"
case a: Array[Int] => "It's an Array[Int]"
case a: Array[String] => "It's an Array[String]"
case a: Array[_] => "It's an array of something other than Int"
case _ => 0
}
println(r1 + ", " + r1.getClass.getName)
}
def main(args: Array[String]): Unit = {
val arr = Array(0, 3, 5)
arr match {
case Array(0, x, y) => println(x + " " + y)
case Array(0) => println("only 0")
//匹配數組以1 開始做爲第一個元素
case Array(1, _*) => println("0 ...")
case _ => println("something else")
}
val lst = List(3, -1)
lst match {
case 0 :: Nil => println("only 0")
case x :: y :: Nil => println(s"x: $x y: $y")
case 0 :: tail => println("0 ...")
case _ => println("something else")
}
val tup = (1, 3, 7)
tup match {
case (1, x, y) => println(s"1, $x , $y")
case (_, z, 5) => println(z)
case _ => println("else")
}
}
注意:在Scala中列表要麼爲空(Nil表示空列表)要麼是一個head元素加上一個tail列表。
9 :: List(5, 2) :: 操做符是將給定的頭和尾建立一個新的列表
注意::: 操做符是右結合的,如9 :: 5 :: 2 :: Nil至關於 9 :: (5 :: (2 :: Nil))
樣例類首先是類,除此以外它是爲模式匹配而優化的類,樣例類用case關鍵字進行聲明。樣例類主要是使用在咱們後面的sparkSQL當中,經過樣例類來映射咱們的表當中的對象
定義形式:
case class 類型,是多例的,後面要跟構造參數。 case class Student(name:String)
case object 類型,是單例的。 case object Person
case class SubmitTask(id: String, name: String)
case class HeartBeat(time: Long)
case object CheckTimeOutTask
//1、樣例類當中的主構造器參數默認爲val的
//2、樣例類當中的apply和unapply方法自動生成
object CaseDemo04 extends App {
val arr = Array(CheckTimeOutTask, HeartBeat(12333), SubmitTask("0001", "task-0001"))
arr(2) match {
case SubmitTask(id, name) => {
println(s"$id,$name")
println(id)
println(name)
println(id+"\t"+name)
}
case HeartBeat(time) => {
println(time)
}
case CheckTimeOutTask => {
println("check")
}
}
}
被包在花括號內沒有match的一組case語句是一個偏函數,它是PartialFunction[A, B]的一個實例,A表明輸入參數類型,B表明返回結果類型,經常使用做輸入模式匹配,偏函數最大的特色就是它只接受和處理其參數定義域的一個子集。
val func1: PartialFunction[String, Int] = {
case "one" => 1
case "two" => 2
// case _ => -1
}
def func2(num: String) : Int = num match {
case "one" => 1
case "two" => 2
case _ => -1
}
def main(args: Array[String]) {
println(func1("one"))
println(func2("one"))
//若是偏函數當中沒有匹配上,那麼就會報錯,咱們能夠經過isDefinedAt來進行判斷
// println(func1("three"))
println(func1.isDefinedAt("three"))
if(func1.isDefinedAt("three")){
println("hello world")
}else{
println("world hello")
}
}
類型參數主要就是研究scala當中的類或者scala當中的方法的泛型
object Demo8 {
def main(args: Array[String]): Unit = {
val result1 = new MyClass("hello",50)
val result2 = new MyClass[Any,Any]("zhangsan","Lisi");
}
}
/**
* 定義一個class類,接收兩個參數,可是兩個參數都是泛型,泛型的類型,會根據咱們
* 建立類的實例化對象的時候,動態的傳遞進行動態的推斷
* @param first
* @param second
* @tparam T
* @tparam B
*/
class MyClass[T,B](first:T,second:B){
println(first+","+second)
}
咱們的函數或者方法,也能夠有類型參數
object methodType{
def getMiddle[T](canshu:T) ={
canshu
}
def main(args: Array[String]): Unit = {
// 從參數類型來推斷類型
println(getMiddle(Array("Bob", "had", "a", "little", "brother")).getClass.getTypeName)
//指定類型,並保存爲具體的函數。
val f = getMiddle[String] _
println(f("Bob"))
}
}
在scala當中,咱們能夠經過上界或者下界來限定咱們泛型的類型,相似於java當中的
? extends T ?號就表示咱們使用的泛型,必須是T類型的子類,這種狀況叫作上界
? super T ?號就表示咱們使用的泛型,必須是T類型的父類,這種狀況叫作下界
在scala當中上界的表示方法使用的是 「<:」, 這個符號就是表示上界,這種形式稱之爲泛型的上界。
在scala當中下界的表示方式使用的是 「>:」, 這個符號就是表示下界,這種形式稱之爲泛型的下界
咱們能夠經過上界的限定,限定咱們傳入的類型必須是某個類型的子類
class Pair1[T <: Comparable[T]](val first: T, val second: T) {
def smaller = if (first.compareTo(second) < 0) first else second
}
object Main1 extends App{
override def main(args: Array[String]): Unit = {
val p = new Pair1("hello", "Brooks")
println(p.smaller)
}
}
咱們能夠經過下界的限定,限定咱們傳入的類型必須是某個類型的父類
class Pair2[T](val first: T, val second: T) {
def replaceFirst[R >: T](newFirst: R) = new Pair2[R](newFirst, second)
override def toString = "(" + first + "," + second + ")"
}
object Main2 extends App{
override def main(args: Array[String]): Unit = {
val p = new Pair2("Nick", "Alice")
println(p)
println(p.replaceFirst("Joke"))
println(p)
}
}
在Java中,T同時是A和B的子類型,稱之爲多界,形式如:<T extends A & B>。
在Scala中,對上界和下界不能有多個,可是可使用混合類型,如:[T <: A with B]。
在Java中,不支持下界的多界形式。如:<T super A & B>這是不支持的。
在Scala中,對複合類型依然可使用下界,如:[T >: A with B]。
說白了就是將咱們的泛型轉化成了具體的類型
在Scala中,若是你想標記某一個泛型能夠隱式的轉換爲另外一個泛型,可使用:[T <% Comparable[T]],因爲Scala的Int類型沒有實現Comparable接口,因此咱們須要將Int類型隱式的轉換爲RichInt類型,好比:
咱們若是須要比較兩個值的大小,那麼咱們的兩個值必須是Comparable的子類,那麼咱們可使用泛型 T <% Comparable 來限制咱們泛型必須是Comparable的子類,而且咱們的泛型在執行真正比較的方法的時候,會根據咱們傳入的類型,自動推斷,進行隱式的轉換,例如咱們傳入4,2 進行比較,那麼咱們會將4, 2 這兩個類型作自動推斷,轉換成真正的RichInt類型而後再繼續進行比較
/**
* 使用 <% 來實現咱們類型的隱式轉換
* @param first
* @param second
* @tparam T
*/
class Pair3[T <% Comparable[T]](val first: T, val second: T) {
def smaller = if (first.compareTo(second) < 0) first else second
override def toString = "(" + first + "," + second + ")"
}
object Main3 extends App {
val p = new Pair3(4, 2)
println(p.smaller)
}
協變和逆變主要是用來解決參數化類型的泛化問題。Scala的協變與逆變是很是有特點的,徹底解決了Java中泛型的一大缺憾;舉例來講,Java中,若是有 A是 B的子類,但 Card[A] 卻不是 Card[B] 的子類;而 Scala 中,只要靈活使用協變與逆變,就能夠解決此類 Java 泛型問題;
因爲參數化類型的參數(參數類型)是可變的,當兩個參數化類型的參數是繼承關係(可泛化),那被參數化的類型是否也能夠泛化呢?Java中這種狀況下是不可泛化的,然而Scala提供了三個選擇,即協變(「+」)、逆變(「-」)和非變。
下面說一下三種狀況的含義,首先假設有參數化特徵Queue,那它能夠有以下三種定義。
(1) trait Queue[T] {}
這是非變狀況。這種狀況下,當類型B是類型A的子類型,則Queue[B]與Queue[A]沒有任何從屬關係,這種狀況是和Java同樣的。
(2) trait Queue[+T] {}
這是協變狀況。這種狀況下,當類型B是類型A的子類型,則Queue[B]也能夠認爲是Queue[A]的子類型,即Queue[B]能夠泛化爲Queue[A]。也就是被參數化類型的泛化方向與參數類型的方向是一致的,因此稱爲協變。
(3) trait Queue[-T] {}
這是逆變狀況。這種狀況下,當類型B是類型A的子類型,則Queue[A]反過來能夠認爲是Queue[B]的子類型。也就是被參數化類型的泛化方向與參數類型的方向是相反的,因此稱爲逆變。
class Anmial
class Dog extends Anmial
協變
List[Dog] extends List[Anmial]
逆變
List[Anmial] extends List[Dog]
不變
List[Dog] = List[Dog]
package cn.itcast.scala.enhance.covariance
class Super
class Sub extends Super
//協變
class Temp1[+A](title: String)
//逆變
class Temp2[-A](title: String)
//非變
class Temp3[A](title: String)
object Covariance_demo{
def main(args: Array[String]) {
//支持協變 Temp1[Sub]仍是Temp1[Super]的子類
val t1: Temp1[Super] = new Temp1[Sub]("hello scala!!!")
//支持逆變 Temp1[Super]是Temp1[Sub]的子類
val t2: Temp2[Sub] = new Temp2[Super]("hello scala!!!")
//支持非變 Temp3[Super]與Temp3[Sub]沒有從屬關係,以下代碼會報錯
//val t3: Temp3[Sub] = new Temp3[Super]("hello scala!!!")
//val t4: Temp3[Super] = new Temp3[Sub]("hello scala!!!")
println(t1.toString)
println(t2.toString)
}
}
注:Scala Actor是scala 2.10.x版本及之前版本的Actor。
Scala在2.11.x版本中將Akka加入其中,做爲其默認的Actor,老版本的Actor已經廢棄。
①Scala中的併發編程思想與Java中的併發編程思想徹底不同,Scala中的Actor是一種不共享數據,依賴於消息傳遞的一種併發編程模式, 避免了死鎖、資源爭奪等狀況。在具體實現的過程當中,Scala中的Actor會不斷的循環本身的郵箱,並經過receive偏函數進行消息的模式匹配並進行相應的處理。
②若是Actor A和 Actor B要相互溝通的話,首先A要給B傳遞一個消息,B會有一個收件箱,而後B會不斷的循環本身的收件箱, 若看見A發過來的消息,B就會解析A的消息並執行,處理完以後就有可能將處理的結果經過郵件的方式發送給A
Scala中的Actor可以實現並行編程的強大功能,它是基於事件模型的併發機制,Scala是運用消息的發送、接收來實現高併發的。
Actor能夠看做是一個個獨立的實體,他們之間是毫無關聯的。可是,他們能夠經過消息來通訊。一個Actor收到其餘Actor的信息後,它能夠根據須要做出各類相應。消息的類型能夠是任意的,消息的內容也能夠是任意的。
對於Java,咱們都知道它的多線程實現須要對共享資源(變量、對象等)使用synchronized 關鍵字進行代碼塊同步、對象鎖互斥等等。並且,經常一大塊的try…catch語句塊中加上wait方法、notify方法、notifyAll方法是讓人很頭疼的。緣由就在於Java中多數使用的是可變狀態的對象資源,對這些資源進行共享來實現多線程編程的話,控制好資源競爭與防止對象狀態被意外修改是很是重要的,而對象狀態的不變性也是較難以保證的。
與Java的基於共享數據和鎖的線程模型不一樣,Scala的actor包則提供了另一種不共享任何數據、依賴消息傳遞的模型,從而進行併發編程。
一、首先調用start()方法啓動Actor
二、調用start()方法後其act()方法會被執行
三、向Actor發送消息
四、act方法執行完成以後,程序會調用exit方法
! |
發送異步消息,沒有返回值。 |
!? |
發送同步消息,等待返回值。 |
!! |
發送異步消息,返回值是 Future[Any]。 |
注意:Future 表示一個異步操做的結果狀態,可能尚未實際完成的異步任務的結果。
Any 是全部類的超類,Future[Any]的泛型是異步操做結果的類型。
注意:若是要開發scala的actor的代碼,那麼須要咱們將scala的SDK添加到咱們的項目當中去
怎麼實現actor併發編程:
一、定義一個class或者是object繼承Actor特質,注意導包import scala.actors.Actor
二、重寫對應的act方法
三、調用Actor的start方法執行Actor
四、當act方法執行完成,整個程序運行結束
import scala.actors.Actor
class Actor1 extends Actor{
override def act(): Unit = {
for(i <- 1 to 10){
println("actor1====="+i)
}
}
}
object Actor2 extends Actor{
override def act(): Unit = {
for(j <- 1 to 10){
println("actor2====="+j)
}
}
}
object Actor1{
def main(args: Array[String]): Unit = {
val actor = new Actor1
actor.act()
Actor2.act()
}
}
說明:上面分別調用了兩個單例對象的start()方法,他們的act()方法會被執行,相同與在java中開啓了兩個線程,線程的run()方法會被執行
注意:這兩個Actor是並行執行的,act()方法中的for循環執行完成後actor程序就退出
怎麼實現actor發送、接受消息
一、定義一個class或者是object繼承Actor特質,注意導包import scala.actors.Actor
二、重寫對應的act方法
三、調用Actor的start方法執行Actor
四、經過不一樣發送消息的方式對actor發送消息
五、act方法中經過receive方法接受消息並進行相應的處理
六、act方法執行完成以後,程序退出
import scala.actors.Actor
class MyActor2 extends Actor{
override def act(): Unit = {
receive{
case "start" => println("starting......")
// case _ => println("我沒有匹配到任何消息")
}
}
}
object MyActor2{
def main(args: Array[String]): Unit = {
val actor = new MyActor2
actor.start()
actor ! "start"
}
}
怎麼實現actor能夠不斷地接受消息:
在act方法中可使用while(true)的方式,不斷的接受消息。
class MyActor3 extends Actor{
override def act(): Unit = {
while (true){
receive{
case "start" => println("starting")
case "stop" =>println("stopping")
}
}
}
}
object MyActor3{
def main(args: Array[String]): Unit = {
val actor = new MyActor3
actor.start()
actor ! "start"
actor ! "stop"
}
}
說明:在act()方法中加入了while (true) 循環,就能夠不停的接收消息
注意:發送start消息和stop的消息是異步的,可是Actor接收到消息執行的過程是同步的按順序執行
使用react方法代替receive方法去接受消息
好處:react方式會複用線程,避免頻繁的線程建立、銷燬和切換。比receive更高效
注意: react 若是要反覆執行消息處理,react外層要用loop,不能用while
class MyActor4 extends Actor{
override def act(): Unit = {
loop{
react{
case "start" => println("starting")
case "stop" => println("stopping")
}
}
}
}
object MyActor4{
def main(args: Array[String]): Unit = {
val actor = new MyActor4
actor.start()
actor ! "start"
actor ! "stop"
}
}
結合case class樣例類發送消息和接受消息
一、將消息封裝在一個樣例類中
二、經過匹配不一樣的樣例類去執行不一樣的操做
三、Actor能夠返回消息給發送方。經過sender方法向當前消息發送方返回消息
case class AsyncMessage(id:Int,message:String)
case class SyncMessage(id:Int,message:String)
case class ReplyMessage(id:Int,message:String)
class MyActor5 extends Actor{
override def act(): Unit = {
loop{
react{
case AsyncMessage(id,message) => {
println(s"$id,$message")
sender ! ReplyMessage(2,"異步有返回值的消息處理成功")
}
case SyncMessage(id,message) =>{
println(s"$id,$message")
sender ! ReplyMessage(id,"我是同步消息的返回值,等到我返回以後才能繼續下一步的處理")
}
}
}
}
}
object MyActor5{
def main(args: Array[String]): Unit = {
val actor: MyActor5 = new MyActor5
actor.start()
actor ! AsyncMessage(1,"helloworld")
val asyncMessage: Future[Any] = actor !! AsyncMessage(2,"actorSend")
val apply: Any = asyncMessage.apply()
println(apply)
println("helloworld22222")
//同步阻塞消息
val syncMessage: Any = actor !? SyncMessage(3,"我是同步阻塞消息")
println(syncMessage)
}
}
需求:
用actor併發編程寫一個單機版的WordCount,將多個文件做爲輸入,計算完成後將多個任務彙總,獲得最終的結果。
大體的思想步驟:
一、經過loop +react 方式去不斷的接受消息
二、利用case class樣例類去匹配對應的操做
三、其中scala中提供了文件讀取的接口Source,經過調用其fromFile方法去獲取文件內容
四、將每一個文件的單詞數量進行局部彙總,存放在一個ListBuffer中
五、最後將ListBuffer中的結果進行全局彙總。
import scala.actors.{Actor, Future}
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.io.{BufferedSource, Source}
case class FileName(path: String)
case class ResultTask(mapWithWord: Map[String, Int])
class WordCount extends Actor {
override def act(): Unit = {
loop {
react {
//使用loop + react的方式接受咱們的數據
case FileName(path: String) => {
//使用Source來讀取文件內容
val file: BufferedSource = Source.fromFile(path)
//獲取文件全部內容
val fileContent: String = file.mkString
// println(fileContent)
//對文件內容進行切分
val split: Array[String] = fileContent.split("\r\n")
// println(split.toBuffer)
//對每一行進行按照空格進行切分
// val map: Array[Array[String]] = split.map(x => x.split(" "))
//切分以後,將數據進行壓平
// val flatten: Array[String] = map.flatten
val flatten: Array[String] = split.flatMap(x => x.split(" "))
val map1: Array[(String, Int)] = flatten.map(x => (x, 1))
// println(map1.toBuffer)
val byKey: Map[String, Array[(String, Int)]] = map1.groupBy(x => x._1)
val values: Map[String, Int] = byKey.mapValues(x => x.length)
sender ! ResultTask(values)
}
}
}
}
}
object WordCount {
def main(args: Array[String]): Unit = {
//申明一個變量,存放咱們的結果數據
val resultTasks = new ListBuffer[ResultTask]
//申明一個set集合用於存放咱們異步發送的返回消息值
val futureSet: mutable.HashSet[Future[Any]] = new mutable.HashSet[Future[Any]]()
//定義咱們須要統計的數據文件路徑
val files: Array[String] = Array("F:\\scala與spark課件資料教案\\2、scala次日\\wordCount\\1.txt", "F:\\scala與spark課件資料教案\\2、scala次日\\wordCount\\2.txt", "F:\\scala與spark課件資料教案\\2、scala次日\\wordCount\\3.txt")
//循環遍歷咱們的數據文件,而後進行發送
for (f <- files) {
val count: WordCount = new WordCount
count.start();
val value: Future[Any] = count !! FileName(f)
futureSet.add(value)
}
while (futureSet.size > 0) {
//過濾咱們的set集合,只取那些有值的set集合
val completeFuture: mutable.HashSet[Future[Any]] = futureSet.filter(x => x.isSet)
for (future <- completeFuture) {
// 調用apply方法,獲取到咱們的future實例,實際上就是ResultTask
val futureApply: Any = future.apply()
//判斷咱們的結果值若是是ResultTask類型的話,那麼咱們就添加到咱們的ListBuffer當中去,表示已經獲取到了返回結果
resultTasks += futureApply.asInstanceOf[ResultTask]
//添加完ListBuffer以後,將set集合當中的元素減小,以便於退出while循環
futureSet -= future
}
}
println(resultTasks)
val flatten: ListBuffer[(String, Int)] = resultTasks.map(x => x.mapWithWord).flatten
val by: Map[String, ListBuffer[(String, Int)]] = flatten.groupBy( x => x._1)
println(by)
//第一個下劃線表示咱們累加以後的結果
// 第二個下劃線表示咱們集合當中每個元組
// _2 表示元組當中第二個元素
val values: Map[String, Int] = by.mapValues(x => x.foldLeft(0)( _ + _._2))
for((k,v) <- values){
println(k+"====>"+v)
}
}
}
def main(args: Array[String]): Unit = {
//注意文件的編碼格式,若是編碼格式不對,那麼讀取報錯
val file: BufferedSource = Source.fromFile("F:\\scala與spark課件資料教案\\3、scala第三天\\files\\file.txt","GBK");
val lines: Iterator[String] = file.getLines()
for(line <- lines){
println(line)
}
//注意關閉文件
file.close()
}
若是要將文件內容轉數組,直接調用toArray便可
若是想將以某個字符或某個正則表達式分開的字符成組讀取,能夠這麼作:
def main(args: Array[String]): Unit = {
val file: BufferedSource = Source.fromFile("F:\\scala與spark課件資料教案\\3、scala第三天\\files\\file2.txt","GBK");
val split: Array[String] = file.mkString.split(" ")
println(split.mkString("\t"))
file.close()
}
一、讀取網絡資源
def main(args: Array[String]): Unit = {
val source: BufferedSource = Source.fromURL("http://www.baidu.com")
val string: String = source.mkString
println(string)
source.close()
}
二、文件寫入操做
def main(args: Array[String]): Unit = {
val writer = new PrintWriter("F:\\scala與spark課件資料教案\\3、scala第三天\\files\\printWriter.txt")
for(i <- 1 to 100){
writer.println(i)
writer.flush()
}
writer.close()
}
三、控制檯交互操做
def main(args: Array[String]): Unit = {
//控制檯交互--老API
print("請輸入內容:")
val consoleLine1 = Console.readLine()
println("剛纔輸入的內容是:" + consoleLine1)
//控制檯交互--新API
print("請輸入內容(新API):")
val consoleLine2 = StdIn.readLine()
println("剛纔輸入的內容是:" + consoleLine2)
}
@SerialVersionUID(1L)
class Person extends Serializable{
override def toString = name + "," + age
val name = "Nick"
val age = 20
}
object PersonMain extends App{
override def main(args: Array[String]): Unit = {
import java.io.{FileOutputStream, FileInputStream, ObjectOutputStream, ObjectInputStream}
val nick = new Person
val out = new ObjectOutputStream(new FileOutputStream("Nick.obj"))
out.writeObject(nick)
out.close()
val in = new ObjectInputStream(new FileInputStream("Nick.obj"))
val saveNick = in.readObject()
in.close()
println(saveNick)
}
}
咱們能夠經過正則表達式匹配一個句子中全部符合匹配的內容,並輸出:
def main(args: Array[String]): Unit = {
import scala.util.matching.Regex
val pattern1 = new Regex("(S|s)cala")
val pattern2 = "(S|s)cala".r
val str = "Scala is scalable and cool"
println((pattern2 findAllIn str).mkString(","))
}
Scala提供的隱式轉換和隱式參數功能,是很是有特點的功能。是Java等編程語言所沒有的功能。它能夠容許你手動指定,將某種類型的對象轉換成其餘類型的對象或者是給一個類增長方法。經過這些功能,能夠實現很是強大、特殊的功能。
Scala的隱式轉換,其實最核心的就是定義隱式轉換方法,即implicit conversion function。定義的隱式轉換方法,只要在編寫的程序內引入,就會被Scala自動使用。Scala會根據隱式轉換方法的簽名,在程序中使用到隱式轉換方法接收的參數類型定義的對象時,會自動將其傳入隱式轉換方法,轉換爲另一種類型的對象並返回。這就是「隱式轉換」。其中全部的隱式值和隱式方法必須放到object中。
然而使用Scala的隱式轉換是有必定的限制的,總結以下:
所謂的隱式參數,指的是在函數或者方法中,定義一個用implicit修飾的參數,此時Scala會嘗試找到一個指定類型的,用implicit修飾的參數,即隱式值,並注入參數。
Scala會在兩個範圍內查找:
(1)Scala默認會使用兩種隱式轉換,一種是源類型或者目標類型的伴生對象內的隱式轉換方法;一種是當前程序做用域內的能夠用惟一標識符表示的隱式轉換方法。
(2)若是隱式轉換方法不在上述兩種狀況下的話,那麼就必須手動使用import語法引入某個包下的隱式轉換方法,好比import test._。一般建議,僅僅在須要進行隱式轉換的地方,用import導入隱式轉換方法,這樣能夠縮小隱式轉換方法的做用域,避免不須要的隱式轉換。
(1)當對象調用類中不存在的方法或成員時,編譯器會自動將對象進行隱式轉換
(2)當方法中的參數的類型與目標類型不一致時
object Chapter14 {
implicit def ConvertDoubleToInt(first:Double):Int= first.toInt
}
object Convert{
//導入隱式轉換的方法
import Chapter14._
def main(args: Array[String]): Unit = {
val first:Int = 3.5
}
}
例如咱們也能夠定義貓和狗,而且讓狗學會抓老鼠的技能
object CatAndDog {
implicit def dogCatchMouse(dog:Dog) = new Cat()
def main(args: Array[String]): Unit = {
val dog = new Dog
dog.catMouse("大黃狗")
}
}
class Cat{
def catMouse(name:String): Unit ={
println(name+"catch a mouse")
}
}
class Dog{
def wangwangwang(name:String) ={
println(name+"看門汪汪汪")
}
}
import java.io.File
import scala.io.Source
object MyPredef{
//定義隱式轉換方法
implicit def file2RichFile(file: File)=new RichFile(file)
}
class RichFile(val f:File) {
def read()=Source.fromFile(f,"GBK").mkString
}
object RichFile{
def main(args: Array[String]) {
val f=new File("F:\\scala與spark課件資料教案\\3、scala第三天\\files\\file.txt")
//使用import導入隱式轉換方法
import MyPredef._
//經過隱式轉換,讓File類具有了RichFile類中的方法
val content=f.read()
println(content)
}
}
class Man(val name:String)
class SuperMan(val name: String) {
def heat=print("超人打怪獸")
}
object SuperMan{
//隱式轉換方法
implicit def man2SuperMan(man:Man)=new SuperMan(man.name)
def main(args: Array[String]) {
val hero=new Man("hero")
//Man具有了SuperMan的方法
hero.heat
}
}
class A(c:C) {
def readBook(): Unit ={
println("A說:好書好書...")
}
}
class B(c:C){
def readBook(): Unit ={
println("B說:看不懂...")
}
def writeBook(): Unit ={
println("B說:不會寫...")
}
}
class C
object AB{
//建立一個類的2個類的隱式轉換
implicit def C2A(c:C)=new A(c)
implicit def C2B(c:C)=new B(c)
}
object B{
def main(args: Array[String]) {
//導包
//1. import AB._ 會將AB類下的全部隱式轉換導進來
//2. import AB._C2A 只導入C類到A類的的隱式轉換方法
//3. import AB._C2B 只導入C類到B類的的隱式轉換方法
import AB._
val c=new C
//因爲A類與B類中都有readBook(),只能導入其中一個,不然調用共同方法時代碼報錯
//c.readBook()
//C類能夠執行B類中的writeBook()
c.writeBook()
}
}
object Company{
//在object中定義隱式值 注意:同一類型的隱式值只容許出現一次,不然會報錯
implicit val aaa="zhangsan"
implicit val bbb=10000.00
}
class Boss {
//注意參數匹配的類型 它須要的是String類型的隱式值
def callName()(implicit name:String):String={
name+" is coming !"
}
//定義一個用implicit修飾的參數
//注意參數匹配的類型 它須要的是Double類型的隱式值
def getMoney()(implicit money:Double):String={
" 當月薪水:"+money
}
}
object Boss extends App{
//使用import導入定義好的隱式值,注意:必須先加載不然會報錯
import Company._
val boss =new Boss
println(boss.callName()+boss.getMoney())
}
目前大多數的分佈式架構底層通訊都是經過RPC實現的,RPC框架很是多,好比前咱們學過的Hadoop項目的RPC通訊框架,可是Hadoop在設計之初就是爲了運行長達數小時的批量而設計的,在某些極端的狀況下,任務提交的延遲很高,因此Hadoop的RPC顯得有些笨重。
Spark 的RPC是經過Akka類庫實現的,Akka用Scala語言開發,基於Actor併發模型實現,Akka具備高可靠、高性能、可擴展等特色,使用Akka能夠輕鬆實現分佈式RPC功能。
Akka基於Actor模型,提供了一個用於構建可擴展的(Scalable)、彈性的(Resilient)、快速響應的(Responsive)應用程序的平臺。
Actor模型:在計算機科學領域,Actor模型是一個並行計算(Concurrent Computation)模型,它把actor做爲並行計算的基本元素來對待:爲響應一個接收到的消息,一個actor可以本身作出一些決策,如建立更多的actor,或發送更多的消息,或者肯定如何去響應接收到的下一個消息。
Actor是Akka中最核心的概念,它是一個封裝了狀態和行爲的對象,Actor之間能夠經過交換消息的方式進行通訊,每一個Actor都有本身的收件箱(Mailbox)。經過Actor可以簡化鎖及線程管理,能夠很是容易地開發出正確地併發程序和並行系統,Actor具備以下特性:
(1)、提供了一種高級抽象,可以簡化在併發(Concurrency)/並行(Parallelism)應用場景下的編程開發
(2)、提供了異步非阻塞的、高性能的事件驅動編程模型
(3)、超級輕量級事件處理(每GB堆內存幾百萬Actor)
利用Akka的actor編程模型,實現2個進程間的通訊。
ActorSystem:在Akka中,ActorSystem是一個重量級的結構,他須要分配多個線程,因此在實際應用中,ActorSystem一般是一個單例對象,咱們可使用這個ActorSystem建立不少Actor。
注意:
(1)、ActorSystem是一個進程中的老大,它負責建立和監督actor
(2)、ActorSystem是一個單例對象
(3)、actor負責通訊
在Akka中,Actor負責通訊,在Actor中有一些重要的生命週期方法。
(1)preStart()方法:該方法在Actor對象構造方法執行後執行,整個Actor生命週期中僅執行一次。
(2)receive()方法:該方法在Actor的preStart方法執行完成後執行,用於接收消息,會被反覆執行。
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<encoding>UTF-8</encoding>
<scala.version>2.11.8</scala.version>
<scala.compat.version>2.11</scala.compat.version>
</properties>
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.11</artifactId>
<version>2.3.14</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-remote_2.11</artifactId>
<version>2.3.14</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<!-- 限制jdk的編譯版本插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
<configuration>
<args>
<arg>-dependencyfile</arg>
<arg>${project.build.directory}/.scala_dependencies</arg>
</args>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>reference.conf</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass></mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
//todo:利用akka的actor模型實現2個進程間的通訊-----Master端
class Master extends Actor{
//構造代碼塊先被執行
println("master constructor invoked")
//prestart方法會在構造代碼塊執行後被調用,而且只被調用一次
override def preStart(): Unit = {
println("preStart method invoked")
}
//receive方法會在prestart方法執行後被調用,表示不斷的接受消息
override def receive: Receive = {
case "connect" =>{
println("a client connected")
//master發送註冊成功信息給worker
sender ! "success"
}
}
}
object Master{
def main(args: Array[String]): Unit = {
val host = args(0)
val port = args(1)
// val host="localhost"
// val port = 8888
//準備配置文件信息
val configStr=
s"""
|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
|akka.remote.netty.tcp.hostname = "$host"
|akka.remote.netty.tcp.port = "$port"
""".stripMargin
//配置config對象 利用ConfigFactory解析配置文件,獲取配置信息
val config=ConfigFactory.parseString(configStr)
// 1、建立ActorSystem,它是整個進程中老大,它負責建立和監督actor,它是單例對象
val masterActorSystem = ActorSystem("masterActorSystem",config)
// 2、經過ActorSystem來建立master actor
val masterActor: ActorRef = masterActorSystem.actorOf(Props(new Master),"masterActor")
// 3、向master actor發送消息
masterActor ! "connect"
}
}
注意:若是使用參數傳遞的方式來接收參數,那麼必須在idea當中配置程序運行傳遞的參數
import akka.actor.{Actor, ActorRef, ActorSelection, ActorSystem, Props}
import com.typesafe.config.{Config, ConfigFactory}
//todo:利用akka中的actor實現2個進程間的通訊-----Worker端
class Worker extends Actor{
println("Worker constructor invoked")
//prestart方法會在構造代碼塊以後被調用,而且只會被調用一次
override def preStart(): Unit = {
println("preStart method invoked")
//獲取master actor的引用
//ActorContext全局變量,能夠經過在已經存在的actor中,尋找目標actor
//調用對應actorSelection方法,
// 方法須要一個path路徑:1、通訊協議、2、master的IP地址、3、master的端口 4、建立master actor老大 5、actor層級
val master: ActorSelection = context.actorSelection("akka.tcp://masterActorSystem@127.0.0.1:8888/user/masterActor")
//向master發送消息
master ! "connect"
}
//receive方法會在prestart方法執行後被調用,不斷的接受消息
override def receive: Receive = {
case "connect" =>{
println("a client connected")
}
case "success" =>{
println("註冊成功")
}
}
}
object Worker{
def main(args: Array[String]): Unit = {
//定義worker的IP地址
val host=args(0)
//定義worker的端口
val port=args(1)
//準備配置文件
val configStr=
s"""
|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
|akka.remote.netty.tcp.hostname = "$host"
|akka.remote.netty.tcp.port = "$port"
""".stripMargin
//經過configFactory來解析配置信息
val config=ConfigFactory.parseString(configStr)
// 1、建立ActorSystem,它是整個進程中的老大,它負責建立和監督actor
val workerActorSystem = ActorSystem("workerActorSystem",config)
// 2、經過actorSystem來建立 worker actor
val workerActor: ActorRef = workerActorSystem.actorOf(Props(new Worker),"workerActor")
//向worker actor發送消息
workerActor ! "connect"
}
}
使用Akka實現一個簡易版的spark通訊框架
需求實現邏輯
一、 啓動master和worker
二、 在worker端對應的preStart方法中拿到master的引用對象,經過這個master引用向master發送註冊信息,註冊信息包含workerId, workCores, workMemory等信息
三、 master接受worker註冊信息,保存註冊信息在一個map集合當中,key爲workerId,value爲註冊信息樣例類。master將worker註冊成功的信息反饋給worker端
四、 worker接受master反饋的註冊成功信息,定時向master發送心跳信息。發送心跳信息,證實worker還活着
五、 master接受worker心跳信息,定時檢查超時worker,並從map當中移除掉超時的worker節點信息
① Master類
package cn.itcast.spark |
② Worker類
package cn.itcast.spark
|
③ WorkerInfo類
package cn.itcast.spark
|
④ 樣例類
package cn.itcast.spark |