Spark也支援Java和Python,爲啥要學Scala?因爲Spark的原生語言是Scala,對Scala的支援最好,我覺得,Scala像是Java和Python的結合體,學着還挺好玩的
val: (變數指向的內容)不可變,宣告必須初始化,不能再賦值
var:(變數指向的內容)可變,宣告需要初始化,可以再賦值
例子:
// import java.lang._ // 預設會匯入java.lang包裡所有的東西,scala裡不用*,用_
val myStr // 會報錯,val變數指向的內容宣告必須初始化
val myStr : String = "Hello World"
val myStr = "Hello World" // 型別寫不寫都行,不寫冒號也不要有,他會自己推斷
println(myStr)
myStr = "Error" // 會報錯,val變數指向的內容是不可變的
var myStr1 = "Hello World"
myStr1 = "Success" // 不會報錯,var變數指向的內容是可變的
<=> Java
java.lang.String myStr = "Hello World"
System.out.println(myStr)
說明:下面 下麪用到符號「<=>」代表「等價於」
(1)Byte,Char,Short,Int,Long,Float,Double,Boolean 都是類
(2)操作符都是方法:
5 + 3 <=> (5).+(3)
var i = 1
i ++ // 沒有 ++,--操作
i += 1
i -= 1
(3)回圈:Range
for(i <- 1 to 5) <=> for(i <- 1.to(5)) <=> for(i <- Range(1,2,3,4,5))
for(i <- 1 until 5) <=> for(i <- Range(1, 2, 3, 4))
for(i <- 1 to 10 by 2) <=> for(i <- Range(1, 3, 5, 7, 9))
for(i <- 0.5f to 5.9f by 0.8f) <=> for(i <- Range(0.5, 1.3, 2.1, 2.8999,
3.699, 4.5, 5.3))
for(i<- 1 to 5 if i%2==0) println(i)
for(i<- 1 to 5; j <- 1 to 3) print(i*j)
//遍歷完把結果儲存下來,生成一個新變數
val r = for (i <- 1 to 5 if i % 2 == 0) yield {println(i); i}
=> r = {2, 4}
(1)讀檔案
import scala.io.Source
val inputFile = Source.fromFile("output.txt")
var lines = inputFile.getLines // 返回一個迭代器
for (line <- lines) println(line)
(2)寫檔案
import java.io.PrintWriter
val out = new PrintWriter(‘output.txt’)
for(i <- 1 to 5) out.println(i)
out.close()
import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException
try{
val f = new FileReader(‘’)
}catch{
case ex: FileNotFoundException =>
//
case ex: IOException =>
//
}finally{
file.close()
}
scala中常用的集合Collection包括 List, Array, Set, Map
集合分爲「可變集合」和「不可變集合」兩種:
可變集合在包scala.collection.mutable
中
不可變集合在包scala.collection.immutable
中
預設的集合是不可變的
構造:
val intList = 1::2::3::Nil <=> val intList = List(1, 2, 3) // Nil代表空列表物件
val strList = ("BigData", "Hadoop", "Spark")
val otherList = "Apache"::strList //::代表在已有列表前端增加元素,右結合
=> otherList = ("Apache", "BigData", "Hadoop", "Spark")
操作
strList.head() //返回第一個值
strList.tail() //返回去掉第一個之後的列表
構造
val university = Map("XMU" -> "Xiamen University")
新增元素
university("FZU") = "Fuzhou"
university += ("TJU" -> "Tianjin University", …)
val iter = Iterator(「a」, 「b」, 「c」)
while(iter.hasNext){}
for(ele <- iter){}
Iterable有兩個方法返回迭代器:grouped和sliding
lst = List(1, 2, 3, 4, 5)
lst grouped 3 // 分塊grouped,將列表以長度3進行分塊
=> .next=List(1, 2, 3), .next()=List(4, 5)
lst sliding 3 // 滑動sliding,視窗長度爲3,每次向後滑動1個單位長度
=> .next=List(1, 2, 3), .next()=List(2, 3, 4), .next()=List(3, 4, 5)
val intArr = new Array[int](3, 4, …) // 一維
val intValueArr = Array(Array(12, 34, 45), …) // 二維
intValueArr(0)(1) = 12 // 下標用圓括號
intValueArr += 10 //相當於append
intValueArr -= 40 //把第一個40刪掉
val t = ("a", 23, 5.2)
print(t._1) // 輸出第一個值
def increment(): Unit = {} //Unit <=> void,標準寫法
def a(value: int): Unit = value += 1 <=> def a(value: int){value += 1} // 方法體只有一句時可以省略{}或者 : Unit =
def a() = {
a // 返回值,不需要寫return
}
(1)先定義個類
class Counter{
def a(): Int = {}
}
val myC = new Counter // 沒參數,可以不寫括號
myC.a() // 這個括號也可以不寫
編譯(在scala直譯器中):
第一種:scala Test.scala
// 定義的是類可以這麼編譯,後面定義Object就不能這麼幹了
第二種:進入到直譯器下面 下麪執行:load ./Test.scala
退出:quit
(2)加上單例物件
class Counter{
def a(): Int = {}
}
object MyCounter{
def main(args:Array[String]){
val myC = new Counter
myC.a()
}
}
有class必須:
scalac Test.scala
scala -classpath . MyCounter // scala -classpath 路徑 類名
沒有class可以:
scala Test.scala
class Counter{
private var privateVal = 0
def value = privateVal // getter
def value_ = (newValue: Int){ // setter
if (newValue>0) privateVal = newValue
}
def a(): Int = {}
}
object MyCounter{
def main(args:Array[String]){
val myC = new Counter
println(myC.value) // 呼叫getter
myC.value = 3 // 呼叫setter
}
}
輔構造器必須呼叫之前已經定義的主構造器或輔構造器
class Counter(val id: Int){ // 主構造器
private var name = ""
private var mode = 1
def a(): Int = {}
def this(id: Int, name: String){
this(id) // 呼叫主構造器
this.name = name
}
def this(id: Int, name: String, mode: Int){
this(id, name) // 呼叫輔構造器
this.mode = mode
}
}
object Person{}
val a = Person() // 不需要new
伴生物件:同一個檔案中,class後面的名稱和object後面的名稱完全一致的時候,class的是伴生類,object是伴生物件,伴生類可以直接呼叫伴生物件裡的方法,伴生物件裏面的所有方法都是靜態方法
編譯之後,伴生物件裡的內容都變成伴生類裡的靜態屬性/方法
有範例之後再用範例呼叫方法纔是呼叫伴生類
apply:用括號傳遞給變數(物件)一個或多個參數時,Scala會把它轉換成對apply的呼叫
update:當對帶有括號幷包括一到若幹參數的物件進行賦值時,編譯器將呼叫物件的update方法,呼叫時,是把括號裡的參數和等號右邊的物件一起作爲update方法的輸入參數來執行呼叫的
(1)重寫一個非抽象方法必須使用override修飾符,抽象方法不需要
(2)只有主構造器可以呼叫超類的主構造器
抽象類:
abstract class Car{ //不能範例化
val carBrand: String // 無初始值,抽象欄位
def info() // 抽象方法,無需abstract
def greeting(){print(…)}
}
子類
class BMWCar extends Car {
override val carbrand="BMW" // 重寫超類欄位,需要使用override關鍵字
def info(){print(…)} // 重寫超類的抽象方法時,不需要override關鍵字
override def greeting(){print(…)} // 重寫超類的非抽象方法,必須使用override關鍵字
}
(1)類似於java裡的介面,但又不僅實現了介面的功能,還具備很多其他特性
(2)是程式碼重用的基本單元,可以同時擁有抽象方法和具體方法
(3)一個類只能繼承自一個超類,卻可以實現多個特質,實現了多重繼承
trait CarId{
var id: Int
def curentId(): Int
}
trait CarGreeting{
def greeting(msg: String){println(..)} // 可以具體實現
}
可以使用extends或with混入類中
class BMWCar extends CarId with CarGreeting{
override var id = 1000
def curentId(): Int = {id += 1; id}
}
(1)例一
val colorStr = colorNum match {
case 1 => "red"
case 2 => "green"
case _ => "not allowed" // other
| case unexpected => unexpected + " is not allowed"
}
(2)例二
for (elem <- List(9, 12.3, "Spark", "Hadoop", "Hello")){
val str = elem match{
case i: Int => i + " is an int value."
case d: Double => d + " is a double value."
case "Spark" => "Spark is found"
case _ => "This is an unexpected value."
}
}
(3)例三
for (elem <- List(9, 1, 2)){
elem match{
case _ if (elem % 2 == 0) => println(elem + " is even.")
case _ => println(elem + " is odd.")
}
}
(4)case 類的匹配
case class Car(brand: String, price: Int)
val myBYDCar = new Car("BYD", 89000)
val myBMWCar = new Car("BMW", 120000)
val myBenzCar = new Car("Benz", 150000)
for (car <- Lisr(myBYDCar, myBMWCar, myBenzCar)) {
car match {
case Car(("BYD", 89000)) => println("")
case Car((brand, price)) => println("Brand: " + brand + ", Price:" + price)
}
}
(5)Option型別
用case類來表示那種可能存在、也可能不存在的值
當預計到變數或者函數返回值可能不會參照任何值的時候,建議使用Option型別
當存在可以被參照的值的時候,返回結果會用Some(Option的子類)來包含這個值,不存在,則返回None
Option型別有個getOrElse("")
方法,使被參照的值不存在時不再顯示None,而是顯示括號裡的提示資訊
Option[T]實際上是個容器,可以看作只包含一個元素或者不包含元素的集合
val books = Map("Hadoop" -> 1, "Spark" -> 2)
books.get("Hadoop")
=> Option[Int] = Some(1)
val sales = books.get("hive").foreach(println)
=> Option[Int] = None
sales.getOrElse("No Such Book")
=> No Such Book
函數位面量:可以體現函數語言程式設計的核心理念
定義函數
def counter(val: Int): Int = {val += 1}
函數型別 (Int) => Int <=> Int => Int
函數值:型別宣告部分去掉,剩下的就是函數的「值」 (val) => {val += 1}
定義函數和定義變數的拆解類比
名稱 | 型別 | 值 | |
---|---|---|---|
val counter: Int => Int = (val) => {val += 1} | counter | Int => Int | (val) => {val += 1} |
val num: Int = 5 | num | Int | 5 |
(num: Int) => num * 2
閉包(還不懂):函數內部可以呼叫函數外部的一些變數值,每次呼叫都會建立一個新閉包
var more = 1
val addMore = (x: Int) => x + more
(1)遍歷
university foreach {case(k, v) => println(k + "+" + v)}
<=> university.foreach({case(k, v) => println(k + "+" + v)})
<=> university foreach {kv => println(kv._1 + "+" + kv._2)}
(2)Map
val books = List("Hadoop", "Hive", "HDFS")
books.map(s => s.toUpperCase) // map操作
=> List("HADOOP", "HIVE", "HDFS")
books.flatMap(s => s.toList) // flatMap操作
=> List("H", "a", …, "H", "I", …, "H", "D", …)
(3)filter
val uni = university filter {kv => kv._2 contains "scala"}
(4)reduce and fold
val list = List(1, 2, 3, 4, 5)
// reduce 歸約:reduceLeft / reduceRight
list.reduce(_ + _) = 15 // 從左往右加 => 1 + 2 + ... + 5
// fold:類似於reduce,只是需要個種子值
list.fold(10)(_ * _) = 1200 // 種子值 * 從左往右乘 => 10 * 1 * 2 * ... * 5