1.基础
高阶函数:将其他函数作为参数或其结果是函数的函数
def apply(f: Int => String, v: Int) = f(v)
def layout(x: Int) = "[" + x.toString() + "]"
println(apply(layout, 10))
部分参数应用函数:先传入部分参数,返回新的部分应用的函数,再调用这个函数,传入其余参数
我们想要多次调用一个方法,具有相同的日期值,但不同的消息值。可以通过将参数部分地应用到 log()方法来消除将日期传递给每个调用的干扰。
def log(date: Date, message: String) = {
println(s"$date, $message")
}
val date = new Date()
val logBoundDate: (String) => Unit = log(date, _: String)
logBoundDate(" hello xixi ")
logBoundDate(" hello juju ")
柯里化
def add(x: Int, y: Int) = x + y
def add(x:Int)(y:Int) = x + y
// 分析下其演变过程
def add(x: Int) = (y: Int) => x + y
flatten函数
val words = Array("hello tom hello jim hello jerry", "hello Hatano")
val splitWords: Array[Array[String]] = words.map(wd => wd.split(" "))
for(i<-splitWords){
println(i.toList)
}
val fatternWords = splitWords.flatten
for(i<-fatternWords){
println(i)
}
等价于flatMap,先map再flatten
val result: Array[String] = words.flatMap(wd => wd.split(" "))
result.foreach(println)
2.集合
Scala 的集合有三大类:序列 Seq、集 Set、映射 Map,所有的集合都扩展自 Iterable 特质
在 Scala 中集合有可变(mutable)和不可变(immutable)两种类型
immutable 类型的集合 初始化后就不能改变了(注意与 val 修饰的变量进行区别).
数组
Array类似java中的数组,长度不可变
ArrayBuffer类似java中的ArrayList,长度可变
val ab = ArrayBuffer[Int]()
//追加一个元素
ab += 1
//追加多个元素
ab += (2, 3, 4, 5)
//追加一个数组++=
ab ++= Array(6, 7)
//追加一个数组缓冲
ab ++= ArrayBuffer(8,9) //打印数组缓冲 ab
ab.insert(0, -1, 0)
ab.remove(8, 2)
Seq 序列
不可变的序列List
val lst1 = List(1,2,3)
//将 0 插入到 lst1 的前面生成一个新的
List val lst2 = 0 :: lst1
val lst3 = lst1.::(0)
val lst4 = 0 +: lst1
val lst5 = lst1.+:(0)
//将一个元素添加到 lst1 的后面产生一个新的集合
val lst6 = lst1 :+ 3
val lst0 = List(4,5,6)
//将 2 个 list 合并成一个新的 List
val lst7 = lst1 ++ lst0
//lst1在前
val lst8 = lst1 ++: lst0
//lst1在后
val lst9 = lst1.:::(lst0)
:: 操作符是右结合的,如9 :: 5 :: 2 :: Nil相当于 9 :: (5 :: (2 :: Nil))
可变的序列ListBuffer
并行化集合:与机器性能 线程数有关
vm虚拟机上分配的是单核cpu
lst0.par.reduce 已经变成了并行化集合,底层会给它分配多个线程进行计算
初始值每个线程都会执行一次,分配的任务数不确定,所以最终结果有多种可能
val lst0 = List(1,7,9,8,0,3,5,4,6,2)
lst0.sum//调用的是foldLeft
lst0.reduce() // 调用的是reduceLeft 从左开始两两叠加讲结果 再和第三个叠加
//返回并行化集合 ParVector(1, 7, 9, 8, 0, 3, 5, 4, 6, 2)
lst0.par
//使用多线程计算,无初始值
lst0.par.reduce(_+_)
//多线程有初始值计算
val lst11 = lst0.par.fold(100)((x, y) => x + y)
val arr = List(List(1, 2, 3), List(3, 4, 5), List(2), List(0))
//最大为420
val result = arr.par.aggregate(100)(_+_.sum, _+_)
Map 和 Option
统计单词出现的次数
val words = Array("hello tom hello star hello sheep", "hello tao hello tom")
words.flatMap(_.split(" "))
.map((_, 1))
.groupBy(_._1)
.mapValues(_.length)
.toList
或
words.flatMap(_.split(" ")).groupBy(x => x).map(t => (t._1, t._2.length)).toList
3.类
伴生对象:当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象。
必须在同一个源文件里定义类和它的伴生对象。
类被称为是这个单例对象的伴生类。
类和它的伴生对象可以互相访问其私有成员。
private var age
1.age 在这个类中是有 getter setter 方法的
2.但是前面加上了 private 修饰, 意味着, age 只能在这个类的内部以及其 伴生类对象中可以访问修改 其他外部类不能访问
类的访问权限:private[包名] class
如:private[sheep] class 代表 student4 在 sheep 包下及其子包下可以见, 同级包 中不能访问
class Student3 private(val name: String, private var age: Int) {
var gender: String = _
// 辅助构造器, 使用 def this
// 在辅助构造器中必须先调用类的主构造器
def this(name: String, age: Int, gender: String) {
this(name, age)
this.gender = gender
}
// private[this]关键字标识该属性只能在类的内部访问, 伴生类不能访问
private[this] val province: String = "北京市"
def getAge = 18
}
// 类的伴生对象
object Student3 {
def main(args: Array[String]): Unit = {
// 伴生对象可以访问类的私有方法和属性
val s3 = new Student3("Angelababy", 30)
s3.age = 29
println(s"${s3.age}")
// println(s"${s3.province}") 伴生类不能访问
}
}
object 类() 默认调用的 apply()方法
「Trait(特质)」 相当于 Java 的接口,实际上它比接口还功能强大。 与接口不同的是,它还可以定义属性和方法的实现。
一般情况下 Scala 的类只能够继承单一父类,但是如果是 Trait(特质) 的话就可以继承多个, 实现了多重继承。
使用 extends 关键字来实现继承其他类或者特质。
「type关键字」
// 把String类型用S代替
type S = String
val name: S = "小星星"
println(name)
可用来定义某种抽象类型:
「样例类」:使用 case 关键字 修饰的类, 其重要的特征就是支持模式匹配
样例类默认是实现了序列化接口的
匹配数组:
object testa extends App{
val arr = Array(1, 1, 7, 0, 2,3)
arr match {
case Array(0, 2, x, y) => println(x + " " + y)
case Array(2, 1, 7, y) => println("only 0 " + y)
case Array(1, 1, 7, _*) => println("0 ...") // _* 任意多个
case _ => println("something else")
}
}
匹配集合
val lst = List(0, 3, 4)
println(lst.head)
println(lst.tail)
lst match {
case 0 :: Nil => println("only 0")
case x :: y :: Nil => println(s"x $x y $y")
case 0 :: a => println(s"value : $a")
case _ => println("something else")
}
匹配样例对象
object testsample extends App {
case class SubmitTask(id: String, name: String)
case class HeartBeat(time: Long)
case object CheckTimeOutTask
val arr = Array(CheckTimeOutTask, new HeartBeat(123), HeartBeat(88888), new HeartBeat(666), SubmitTask("0001", "task-0001"))
val i = Random.nextInt(arr.length)
println(i)
val element = arr(i)
println(element)
element match {
case SubmitTask(id, name) => {
println(s"$id, $name")
}
case HeartBeat(time) => {
println(time)
}
case CheckTimeOutTask => {
println("check")
}
}
}
4.并发编程
Akka 介绍
写并发程序很难。程序员不得不处理线程、锁和竞态条件等等,这个过程很容易出错。
而且 会导致程序代码难以阅读、测试和维护。
Actor 模型
Akka 处理并发的方法基于 Actor 模型。在基于 Actor 的系统里,所有的事物都是 Actor。
Actor 与 Actor 之间只能通 过消息通信。
为什么 Actor 模型是一种处理并发问题的解决方案?
单线程处理,简化并发编程,提升程序性能。
从图中可以看到,Actor 与 Actor 之前只能用消息进行通信,当某一个 Actor 给另外一个 Actor发消息,消息是有顺序的,只需要将消息投寄的相应的邮箱,至于对方 Actor 怎么处理你的 消息你并不知道,当然你也可等待它的回复。
Actor 是 ActorSystem 创建的,ActorSystem 的职责是负责创建并管理其创建的 Actor,ActorSystem 的单例的,一个 JVM 进程中有一个即可,而 Acotr 是多例的。