学习视频:https://www.bilibili.com/video/BV1oJ411m7z3
Scala是一种多范式编程语言,设计初衷是集成面向对象编程和函数式编程的各种特性
Scal特性
基于JVM
- 运行在jvm之上,可以和java混编;
- scala可以调用java的包,Java也可以调用Scala的包
类型推测
-
不需要显式的定义变量的是数据类型,可以自动推测出来
-
var a = 100; val b = 200.0;
-
var
定义变量,val
定义常量
并发和分布式
- 使用Actor作为其并发模型。Actor是一种类似于线程的实体
Trait(特性)
- 类似于java中 interface 和 类 的结合
- 综合了接口的灵活性和类的强大功能
模式匹配
- 类似于
switch case
,只不过功能更加强大
高阶函数
- 操作其他函数的函数
- 甚至可以用其他函数作为当前函数的参数或输出结果
Scala基础
基本语法
-
定义常量使用 val,定义变量使用 var
-
每行后面可以不写分号
-
大小写敏感
-
类名首字母大写,方法名首字母小写
-
s"$i * $j =" + i * j"
/* for */ val r = (1 to 10) //1 --10 val r1 = 1 until 10 //1 --9 for (i <- 1 to 10){ println(i) } /* if else */ val i = 50 if (i<30){ println("i<30") }else if(i>30&&i<60){ println("i<30 && i<60") }else{ println("i>60") } //打印99乘法表 // for (i <- 1.until(10)){ // for (j <- 1.until(10)){ // if(i>=j){ // print(s"$i * $j ="+i*j+"\t") // } // if(i==j){ // println() // } // } // } for (i <- 1.until(10);j <- 1.until(10)) { if (i >= j) { print(s"$i * $j =" + i * j + "\t") } if (i == j) { println() } }
数据类型
Byte | 一个字节 -128 --127 |
---|---|
Short | 两个字节 |
Int | 四个字节 |
Long | 八个字节 |
Float | 32位单精度浮点型 |
Double | 64位双精度浮点型 |
Char | 两个字节字符 |
Boolean | true、false |
Unit | 表示无值,和void等同 |
String | 字符串 |
Null | 空值或者空引用 |
Nothing | 其他所有类型的子类,由于scala会类型推测,当无法推测时就判定为Nothing |
Any | 其他所有类型的父类 |
AnyRef | 所有引用类型的父类 |
AnyVal | 所有值类型的父类(前9项,String之前) |
以上数据类型都是封装后的对象,首字母全部大写
Null | Trait,唯一实例是null,是AnyRef的子类 |
---|---|
Nothing | Trait,所有类型的子类,没有实例 |
None | Option的两个子类之一,用于安全的函数返回值 |
Unit | 无返回值的函数类型(等同于java中的void) |
Nil | 长度为0的list |
类和对象
对象
-
对象object,相当于类的单例对象,不需要使用new实例化。
-
其中定义的默认都是静态的。所以其中可以直接写main方法。
-
对象中的构造器会在第一次使用时会被执行,如果一个对象从未被调用,他的构造器也不会执行。
-
object不能传递参数;如果非要传参,需要在其中使用apply方法
object Test{ def apply(i: Int) = { println("i is:"+i) } def sout(): Unit ={ println("test") } }
类
-
类class,需要使用new来实例化。
-
可以传参,传参一定要指定类型,有了参数就默认有构造函数,类中的属性默认有了getter、setter方法。
-
而且类中可以直接写执行代码(不需要在外层封装方法)(
println
)。 -
类构造的时候,除了方法不执行(构造方法除外),其他都执行
-
类中的属性可以用private修饰,表示为私有属性
-
在同一个scala文件中,class名称与object名称一样时,这个类叫做对象的伴生类,对象叫做类的伴生对象。他们之间可以互相访问私有变量
//重写构造this(),构造中的第一行必须调用默认构造 this() class Person(xname:String,xage:Int){ var name = xname val age = xage var gender = 'M' println(name) def this(yname:String,yage:Int,ygender:Char){ this(yname,yage) gender = ygender } def sout(): Unit ={ println(name,age) } } //object中是静态的,所以可以写main函数 object HelloWorld { def main(args: Array[String]): Unit = { val person = new Person("zhangsan", 18) person.name="lisi" person.sout() Test(5) println("hello world!") } }
方法与函数
方法定义
def max(a:Int,b:Int): Int ={
if(a>b){
return a
}else{
return b
}
}
def max(a:Int,b:Int)={
if(a>b){
a
}else{
b
}
}
def min(a:Int,b:Int) = if(a>b) b else a
- 定义方法使用
def
关键字,格式def 方法名(参数列表:类型):返回值类型={方法体}
- 方法体中最后返回值可以使用
return
,如果使用return,返回类型一定要指定 - 如果方法体中没有 return ,默认将方法体中最后一行计算的结果返回,返回类型可以省略
- 方法体如果可以一行解决,
{...}
可以去掉 - 如果定义方法时,方法名和方法体之间的
=
被省略了,方法最后会返回空值unit
递归方法
def func(a:Int): Int ={
if(a==1){
a
}else{
a*func(a-1)
}
}
- 递归方法要显式的指明返回类型
默认值参数的方法
//定义
def add(a:Int=10,b:Int=20)={
a+b
}
//调用
add()
add(100, 200)
add(100)
add(b=100) //只修改某个参数
- 在参数列表中给定初值
变长参数方法
def func(s:String*)={
println(s)
}
func("hello","a","b")
- 在参数列表类型后面加上
*
,Int* String*
匿名函数
- 以
()=>{}
形式出现的就叫匿名函数,不需要函数名 - 只要出现
=>
,就是匿名函数 - 多用于方法的参数是函数时
def func(s:String*)={
s.foreach(el => println(el))
}
def fun =(a:Int,b:Int)=>{a+b}
高阶函数
- 方法参数是函数、方法返回是函数或者两者都是函数,的方法叫做高阶函数
//1. 参数是函数
def fun(a:Int,b:Int)={
a+b
}
def func(f:(Int,Int)=>Int,s:String)={
val i = f(100, 200)
s"$i+$s"
}
//调用
func(fun,"hello")
func((a:Int,b:Int)=>a*b,"hello")
//2.返回是函数
//显式声明返回类型是函数
def func(s:String):(String,String)=>String={
def fun(a:String,b:String): String ={
s+a+b
}
fun
}
//不显式声明返回类型
def func(s:String)={
def fun(a:String,b:String): String ={
s+a+b
}
//加个下划线表示 将方法转成函数
fun _
}
//调用
func("he")("ll","o")
//3. 两者都是函数
def func(f:(Int,Int)=>Int):(String,String)=>String = {
val i = f(1, 2)
def fun(a:String,b:String):String={
s"$i + a + b"
}
fun
}
func((a,b)=>a+b)("hello","word")
方法加个下划线表示可以强制将方法转成函数
def add(a:Int=10,b:Int=20)={
a+b
}
val function1: (Int, Int) => Int = add _
柯里化函数
def func(a:Int,b:Int)(c:Int,d:Int) = {
a+b+c+d
}
func(1,2)(3,4)
- 高阶函数三的简化版,隐式转换时要用
字符串和集合
字符串String
String即为java中的String,只是增添了有一些方法
String a = "abcde"
String b = "ABCDEF"
//忽略大小写是否相等
a.equalsIgnoreCase(b)
数组Array
val arr = Array[String]("a","b","c")
//定义长度为3的数组arr1
val arr1 = new Array[Int](3)
arr1(0) = 2;
//打印
arr.foreach(elem=>println(elem))
arr.foreach(println(_))
arr1.foreach(println)
//可变长数组
var arr2 = ArrayBuffer[Int](1,2,3)
// append 等同于 += ,都是从后面追加
arr2.append(6)
arr2.+=(7)
// +=: 则是在前面追加
arr2.+=:(8)
arr2.foreach(println)
列表List
val list = List[String]("hello world","hello scala")
//map方法 进一条数据输出一条数据
//输出数据类型为 List[Array[String]]
val value1: List[Array[String]] = list.map(s => {
s.split(" ")
})
value1.foreach(s => s.foreach(println))
//flatmap方法 进一条数据输出多条数据
//输出数据类型为 List[String]
val value2: List[String] = list.flatMap(s => {
s.split(" ")
})
value2.foreach(println)
//filter方法,过滤器,匹配上了就保留,否则舍弃
val value3: List[String] = list.filter(s => {
"hello world".equals(s)
})
value3.foreach(println)
//可变长列表list
var list2 = ListBuffer[Int](1,2,3)
// append 等同于 += ,都是从后面追加
list2.append(6)
list2.+=(7)
// +=: 则是在前面追加
list2.+=:(8)
list2.foreach(println)
集合Set
val set = Set[Int](1,2,3,4)
//无序,去重
set.foreach(println)
val set1 = Set[Int](3,4,5,6)
// 并集
set.concat(set1).foreach(println)
//交集
set.intersect(set1).foreach(println)
//差集
set.diff(set1).foreach(println)
//可变set
//import scala.collection.mutable.Set
//val set2 = Set[Int](1,2,3)
import scala.collection.mutable
val set2 = mutable.Set[Int](1,2,3)
set2.add(5)
set2.foreach(println)
//import scala.collection.immutable
//不可变
val set3 = immutable.Set[Int](1,2,3)
set3.foreach(println)
Map
val map = Map[String, Int]("a" -> 100, "b" -> 200, "c" -> 300,"e"->500)
val map2 = Map[String,Int](("a",1),("b",2),("c",3),("d",4))
map.foreach(println)
val value1: Option[Int] = map.get("a")
println(value1)
val value2: Option[Int] = map.get("a1")
println(value2)
val keys: Iterable[String] = map.keys
val values: Iterable[Int] = map.values
// map2追加到map中
map.++(map2).foreach(println)
// map追加到map2中
map.++:(map2).foreach(println)
//可变长Map
val map3 = mutable.Map[String,Int](("a",1),("b",2))
map3.put("c",3)
map3.filter(tem=>{
val key = tem._1
var value = tem._2
value >=2
}).foreach(println)
元组Tuple
()
中放入一堆元素就叫元组,元素类型可同可不同- 元组最多支持22个元素
- 元组可new可不new,甚至可以直接写在括号中
- 只有二元元组能翻转
swap
val tuple1: Tuple1[String] = new Tuple1("hahha")
val tuple2: (String, Int) = Tuple2("ss", 20)
val tuple4 = (1, "ma", true, 'C')
val tuple10 = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
//打印元组
println(tuple4._3)
println(tuple4)
//遍历元组
val iterator: Iterator[Any] = tuple4.productIterator
iterator.foreach(println)
//二元元组的翻转
println(tuple2)
println(tuple2.swap)
Trait
- Trait 相当于java接口与抽象类的结合体,既可以有方法体的方法,也可以有没有方法体的方法
- Trait 不能传参
- 继承第一个 Trait 用
extends
关键字,后面再继承其他 Trait 用with
关键字
trait Listen{
def listening(name:String)={
println(s"$name is listening...")
}
}
trait Speak{
def speaking(name:String): Unit ={
println(s"$name is speaking...")
}
}
class Human(xname:String) extends Speak with Listen {
val name = xname
}
object TraitTest {
def main(args: Array[String]): Unit = {
val human1 = new Human("zhangsan")
human1.speaking(human1.name);
human1.listening(human1.name);
}
}
trait IsEqual{
def isEqual(o:Any):Boolean
def isNotEqual(o:Any):Boolean={
!isEqual(o)
}
}
class Point(xx:Int,yy:Int) extends IsEqual {
val x = xx
val y = yy
override def isEqual(o: Any): Boolean = {
o.isInstanceOf[Point] && o.asInstanceOf[Point].x==this.x
}
}
val point1 = new Point(1,2);
val point2 = new Point(1, 3)
println(point1.isEqual(point2))
模式匹配Match
基础匹配
case _
相当于default
,什么都能匹配上,要放到最后match
可以匹配值还可以匹配类型- 匹配过程中会有数值的转换
- 从上往下匹配,匹配上了就自动终止
- 模式匹配外部的
{ }
可以省略
object MatchTest {
def main(args: Array[String]): Unit = {
val tuple = (1, 2, 1.2, true, 'c', "abc")
val ite = tuple.productIterator
// ite.foreach(i=>{
// matchTest(i)
// })
ite.foreach(matchTest)
}
def matchTest(o:Any)={
o match {
case 1 => println("value is 1")
case i:Int => println(s"type is Int,value is $i")
case d:Double =>println(s"type is Double,value is $d")
case 'c' =>println("value is Char")
case _ => println("no match ...")
}
}
}
偏函数
- 模式匹配中,只有
case
没有match
,可以将其定义为偏函数 - 偏函数定义时,不能使用
()
传参,要使用PartialFunction中传入一个值 - 函数格式为
def 函数名:PartialFunction[输入类型,输出类型] = {case => }
object PartialFun {
def myTest :PartialFunction[String,Int]={
case "a" => 1
case "b" => 2
case "c" => 3
case _ => 10
}
def main(args: Array[String]): Unit = {
println(myTest("d"))
println(myTest("b"))
}
}
样例类
- 使用关键字
case
修饰的类,就叫做样例类 - 该类默认实现了
getter、setter、toString、equals、 copy、hashCode
等方法 - 样例类可以 new 也可以不 new
case class Animals(sname:String,sage:Int){
val name = sname
val age = sage
}
object CaseClass {
def main(args: Array[String]): Unit = {
val a1 = Animals("miao", 2)
println(a1)
println(a1.hashCode())
}
}
隐式转换
定义
- 在Scala编译器进行类型匹配时,如果找不到合适的类型
- 那么隐式转换会让编译器在作用范围内自动推导出来合适的类型
隐式值与隐式参数
- 在参数前面加上
implicit
,就会自动在同一个作用域(同一个类或对象)中查找隐式的同类型的参数 - 同类型的参数的隐式值只能在作用域中出现一次
- 如果方法只有一个参数且为隐式参数时,可以直接用
implicit
定义该参数,调用时直接创建类型不传入参数就可以 - 如果方法中有部分参数需要隐式转换,那么该方法需要使用柯里化的方式定义,而且隐式参数必须放在第二个括号中
object ImplicitTest {
def printName(implicit name:String): Unit ={
println(s"the name is $name")
}
//部分参数需要隐式转换
def printNameAndAge(age:Int)(implicit name:String)={
println(s"the name is $name,the age is $age")
}
def main(args: Array[String]): Unit = {
implicit val name1 = "zhangsan"
printName
printName("lisi")
printNameAndAge(15)
printNameAndAge(15)("lisi")
}
}
隐式转换函数
- 使用
implicit
修饰的方法就叫隐式转换函数 - 假设A类型变量调用了 B方法,但是A类型中没有B方法,但是C类型有B方法;
- 那么编译器就会在作用域中查找有没有隐式转换函数将A类型转换成C类型,如果有,则A类型可以调用B方法
- 隐式转换函数只与函数的参数类型和返回类型有关,与函数名称无关,所以作用域中不能有其他相同参数类型和返回类型的隐式转换函数
class Animal(name:String){
def canFly()={
println(s"$name can fly")
}
}
class Bird(xname:String){
val name = xname
}
object ImplicitFunc {
implicit def birdToAnimal(b:Bird)={
new Animal(b.name)
}
def main(args: Array[String]): Unit = {
val bird = new Bird("eeee")
bird.canFly()
}
}
隐式类
- 使用
implicit
定义的类,只能定义在对象、类中 - 若一个变量A没有某种方法或变量,而这个类A可以调用某些方法或者某些变量时,可以定义一个隐式类,隐式类中定义这些方法或变量,隐式类中传入A即可
- 隐式类的构造必须只有一个参数,同一作用域中不能出现同类型构造的隐式类
class Bird1(xname:String){
val name = xname
}
object ImplicitClass {
implicit class Animal1(b:Bird1){
def canFly()={
println(s"${b.name} can fly")
}
}
def main(args: Array[String]): Unit = {
val b = new Bird1("aaaa")
b.canFly()
}
}
WordCount
val conf = new SparkConf().setMaster("local").setAppName("wordCount")
val sc = new SparkContext(conf)
val file = sc.textFile("data/hahah.text")
file.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect().foreach(println)
sc.stop()