Spark也支持Java和Python,为啥要学Scala?因为Spark的原生语言是Scala,对Scala的支持最好,我觉得,Scala像是Java和Python的结合体,学着还挺好玩的

一、基本语法

1.声明值和变量:

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)

2.基本数据类型和操作

说明:下面用到符号“<=>”代表“等价于”
(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}

3.读写文件

(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()

4.异常处理

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
默认的集合是不可变的

1.List

构造:

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()	//返回去掉第一个之后的列表

2.Map

构造

val university = Map("XMU" -> "Xiamen University")

添加元素

university("FZU") = "Fuzhou"
university += ("TJU" -> "Tianjin University", …)

3.迭代器

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)

4.数组:可变,可索引,相同类型

长度确定用Array,不确定用ArrayBuffer,元素类型可不同,有点像python里的list

val intArr = new Array[int](3, 4, …) // 一维
val intValueArr = Array(Array(12, 34, 45), …)	// 二维
intValueArr(0)(1) = 12	// 下标用圆括号
intValueArr += 10 //相当于append
intValueArr -= 40 //把第一个40删掉

5.元组:不同类型

val t = ("a", 23, 5.2)
print(t._1)		// 输出第一个值

三、类

1.方法

def increment(): Unit = {}	//Unit <=> void,标准写法
def a(value: int): Unit = value += 1	<=> 	def a(value: int){value += 1}	// 方法体只有一句时可以省略{}或者 : Unit =
def a() = {
	a	// 返回值,不需要写return
}

2.编译和执行

(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

3. getter/setter方法

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
	}
}

4. 构造器

辅构造器必须调用之前已经定义的主构造器或辅构造器

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
	}
}

5. 单例对象:不定义类也行,类似于java里的static

object Person{} 	
val a = Person() // 不需要new

伴生对象:同一个文件中,class后面的名称和object后面的名称完全一致的时候,class的是伴生类,object是伴生对象,伴生类可以直接调用伴生对象里的方法,伴生对象里面的所有方法都是静态方法
编译之后,伴生对象里的内容都变成伴生类里的静态属性/方法
有实例之后再用实例调用方法才是调用伴生类

6. apply 和 update

apply:用括号传递给变量(对象)一个或多个参数时,Scala会把它转换成对apply的调用
update:当对带有括号并包括一到若干参数的对象进行赋值时,编译器将调用对象的update方法,调用时,是把括号里的参数和等号右边的对象一起作为update方法的输入参数来执行调用的

7. 继承

(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关键字
}

8. 特质 trait

(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}
}

9. 模式匹配

(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

10. 函数的类型和值

函数字面量:可以体现函数式编程的核心理念
定义函数

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

11. 匿名函数: lambda表达式

(num: Int) => num * 2

闭包(还不懂):函数内部可以调用函数外部的一些变量值,每次调用都会创建一个新闭包

var more = 1
val addMore = (x: Int) => x + more

12. 占位符语法:下划线 _

13. 集合的操作

(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