文章目录
一 Option类型
Option类型用来表示可能有值, 也可能没有值, 有两个子类
- None—>无值
- Some—>有值
class OptionDemo {
def main(args: Array[String]): Unit = {
//option中的参数相当于是一个元组,
val op: Option[(String, Int, Boolean)] = Some("小明", 18, true)
//在Map中, 有两种取值方法-->get/getOrElse
//若实际操作的是Option, name就使用getOrElse
//使用元组接收数据
val value: (String, Int, Boolean) = op.getOrElse(null)
val map = Map(("xiaoli", 16), ("xiaozhang", 23), ("xiaosong", 43))
val v: Option[Int] = map.get("xiaoli")
v match {
case Some(a) => println(a)
case None => println("没有值")
}
}
}
偏函数
定义的方法实现体被包裹在一个大{} 内, 并且没有实现match关键字,而在实体中添加case class这样的方法叫做偏函数
def 方法名(参数列表) 返回值类型=>{
case 条件=>逻辑
//可以有很多case
}
//偏函数
object PianFuncDemo {
//在不使用偏函数的前提下也可以完成(普通模式)
def m1(num: String): Int = num match {
case "one" => 1
case "two" => 2
case _ => -1
}
//这里还可以进一步简化-->偏函数
//PartialFunction这是一个固定名字, 代表偏函数
//第一个数据类型是参数, 即匹配条件的数据类型
//第二个数据类型是返回值类型的数据类型即得到的结果
def m2: PartialFunction[String, Int] = {
case "one" => 1
case "two" => 2
case _ => -1
}
def main(args: Array[String]): Unit = {
//调用偏函数方法
println(m1("one")) //print 1
println(m2("two")) //print 2
}
}
二 获取线程池中的返回值Future
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class FutureDemo {
// 线程池有4种创建的方式, 这里只演示一种
public static void main(String[] args) {
/**
* 1. 创建固定大小的线程池
* 创建固定大小线程池的时候, 建议genuine系统剩余线程数量类决定线程池的大小
* int i = Runtime.getRuntime().availableProcessors();
* ExecutorService threadPool = Executors.newCachedThreadPool();
* 2. 创建一个可以缓存的线程, 如果线程长度超过了处理需求, 可以灵活的回收空闲线程
* 如果没有后台线程会自动创建
* 线程池是无限大的, 当执行第二个任务的时候, 若第一个任务已,经执行完成,会重复使用第一个执行任务的线程, 不会再重新创建
*/
ExecutorService threadPool = Executors.newCachedThreadPool();
//关闭线程池, threadPool.shutdown();
//获取线程池中产生的数据
//Future相当于是一个容器, 可以封装获取的数据
Future<String> future = threadPool.submit(new Callable<String>() {
//和线程创建中的run方法类似
@Override
public String call() throws Exception {
System.out.println("Thread Name is " + Thread.currentThread().getName() + "\nThread ID is " + Thread.currentThread().getId());
System.out.println("正在获取数据");
System.out.println("正在读取数据");
Thread.sleep(1000);
System.out.println("读取完毕");
return "success";
}
});
//主线程需要睡眠, 否则会直接执行到最后, 导致get获取的时候没有值
try {
Thread.sleep(2000);
if (future.isDone()) {
//get方法如果获取到异常会被捕获
} else {
System.out.println("没有值");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
三 Actor
并行: 同一时间点多个线程一起执行
并发: 同一时间段多个线程一起执行
Actor可以实现并行编程, 他是基于事件模型的并发机制, 运用Actor的消息收发机制实现多线程并行编程, Spark中使用AKKA的通信模型, 这里使用到Actor进行消息传送
看源码可知, Master和Worker之间的消息传递就是使用AKKA(AKKA实现的Actor)
3.1 特点:
- Actor数据不能共享
- 没有锁的概念,
- Actor之间通过信息进行通信
3.2 Actor发送消息的三种方式
- ! 发送异步消息, 没有返回值
- !? 发送同步消息, 有返回值, 需要进行线程等待
- !! 发送异步消息, 有返回值, 返回值使用Future[Any]接收
3.3 异步和同步
- 同步调用的时候会等待响应会返回值, 若没有响应会返回值, 就会阻塞线程, 不继续执行
- 异步调用就是单独的进行一个线程执行, 原始线程启动异步调用, 异步调用会调用另外一个线程执行请求, 原始线程不会等待这个异步线程, 彼此之间是用时执行, 只需异步执行完成后通知即可
3.4 案例
3.4.1 开启Actor
Actor执行顺序:
- 调用start 方法来启动Actor
- 执行Actor中act方法
- 向Actor发送信息(打印)
object ActorDemo1 {
def main(args: Array[String]): Unit = {
//启动Actor
MyActor.start()
}
}
import scala.actors.Actor
object MyActor extends Actor{
//act 相当于Actor中的执行方法
override def act(): Unit = {
for (i <- 1 to 10){
//打印值
println("actor"+i)
//做延迟, 防止开始即结束
Thread.sleep(1000)
}
}
}
3.4.2 使用消息发送机制发送消息
import scala.actors.Actor
object MyActor2 extends Actor{
override def act(): Unit = {
//死循环
while(true){
//接收数据
receive{
//类似于偏函数的形式
//因为需要传递信息, 信息中可能会包含着一些数据, 所以此时使用偏函数的形式是最合理的, 因为可以匹配消息类型
//既然可以获取消息数据(即传递过来的信息), 所以使用样例类最合理
case AsyncMsg(id, msg) =>{
//接收异步消息
println("id: "+id+" msg: "+msg)
//接收到信息之后的回传信息
//异步发送不需要返回值(已经接收到信息, 无需返回)
sender ! ReplayMsg(0,"success")
}
case SyncMsg(id,msg)=>{
//接收异步消息
println("id: "+id+" msg: "+msg)
//接收到信息之后的回传信息
//异步发送不需要返回值(已经接收到信息, 无需返回)
sender ! ReplayMsg(1,"success")
}
}
}
}
}
//创建异步消息样例类
case class AsyncMsg(id:Int, msg:String)
//同步
case class SyncMsg(id:Int,msg:String)
//回传信息使用的样例类(异步或同步发送的时候会有返回值, 所以定义一个类进行消息发送)
case class ReplayMsg(id:Int,msg:String)
import scala.actors.Future
object ActorDemo2 {
def main(args: Array[String]): Unit = {
//启动Actor同时可以获取Actor对象
//获取Actor目的就是为了发送消息
val actor = MyActor2.start()
//1. 发送异步消息无返回值
actor ! AsyncMsg(1,"发送异步消息无返回值")
//因为没有返回值, 所以只能自行打印
println("异步发送消息, 没有返回值")
//2. 发送同步消息, 有返回值, 会等待线程
val value: Any = actor !? SyncMsg(2,"发送同步消息, 有返回值")
println("同步消息发送完成")
println(value)
//3. 发送异步消息, 有返回值, 返回值是Any类型
val values: Future[Any] = actor !! AsyncMsg(3,"异步消息,有返回值")
//休眠一下
Thread.sleep(1000)
//判断是否有数据
if(values.isSet){
//取出数据
val applyMsg: Any = values.apply()
println(applyMsg)
}else{
println("Nothing")
}
}
}
四 高阶函数
- 高阶函数其实就是作为方法中参数的传递(匿名函数, 闭包, 柯里化, 隐式转换函数), 函数作为方法的参数
- [官方]高阶函数可以实现AOP(面向切片), 可以理解为面向对象的一种补充
4.1函数作为方法的参数
def 方法名(函数名(即方法的形参名): 函数中的形参的数据类型=>函数的返回值类型)
def method:(f:Int=>String,v:Int)={
//使用函数就相当于调用函数
f(v)
}
object HighFuncDemo {
def method (f:Int=>String,v:Int)={
f(v)
}
def main(args: Array[String]): Unit = {
val str: String = method(x=>x.toString,5)
println(str) //print 5
}
}
4.2作为函数值的演示:函数的定义, 调用, 转换
object HighFuncDemo {
def main(args: Array[String]): Unit = {
//定义函数, 多条语句使用{ }
val f1 = (x: Int) => x * 2
//另外一种版本
val f2: Int => Int = x => x * 2
//多个参数
val f3: (Int, Int) => Int = (x, y) => (x + y)
//调用函数 函数名(参数) 如果函数没有返回值不建议接收
val res1: Int = f1(1)
//将当前数组每个元素*2
val arr = Array(1, 2, 3, 4)
val arr2: Array[Int] = arr.map(f2)
//使用匿名函数
arr.map((x: Int) => {
x * 2
})
//简化
arr.map(_ * 2)
//转换: 将方法转换成函数
//1. 显式转换
def show() = {
println("将要转换的方法")
}
val f4: () => Unit = show _
f4()
//2. 隐式转换 在调用系统提供API的时候m,参数可以是方法, 也可以是函数, 若是方法会自动转换
def m(x: Int): Unit = {
x * 2
}
arr.map(m)
}
}
五 闭包
闭包是一个函数, 返回值依赖于声明在函数外部的一个或多个变量. 闭包通常来讲, 可以简单的认为是可以访问一个函数里边的局部变量另外一个函数
object ClosureDemo {
def show(msg:String)=(name:String)=>println(msg+name)
def main(args: Array[String]): Unit = {
var i = 3
val func=(j:Int)=>j*i
println(func(3))
//调用show方法
//先将方法中的函数获取出来
val showA = show("hello")
//传入参数, 执行show方法
showA("world")
}
}
六 柯里化
柯里化就是把接收参数的函数转变成一个单一参数的函数
目前所接触过的函数中带有两个参数的有, fold(默认值)(计算逻辑)
柯里化的应用其实还有map, 其实map也是有两个参数的,但是第二个参数是一个隐式转换, 所以只传入一个参数即可
下面的代码只能进行模拟, 实际的应用时在源码中
object CurryingDemo {
def main(args: Array[String]): Unit = {
//定义一个方法 求两个数的和
def curry(x: Int, y: Int) = x + y
//调用
println(curry(1, 2))
//将当前的方法转换成柯里化的形式
def curry2(x: Int)(y: Int) = x + y
//调用
println(curry2(1)(2))
//上边的方法需要传入两个参数, 现在需要将参数进行减少, 但是还不影响计算, 对外只需传入一个参数,
//implicit--->关键字, 可以进行隐式转换, 作为参数的时候无需调用, 可以自动执行
def curry3(x:Int)(implicit y:Int=2)=x+y
println(curry3(1))
//现阶段柯里化的应用有fold和map
}
}
- 柯里化第二种方式–>闭包形式, 返回值是一个匿名的函数, 这个形式的应用在源码中有很多, 和正常的柯里化使用量是相同的
- 函数的返回值是原有的第二个参数
object CurryingDemo2 {
def curry(x:Int)=(y:Int)=>x+y
def main(args: Array[String]): Unit = {
val func: Int => Int = curry(2)
println(func(2))
}
}
七 隐式转换
隐式转换其实就是添加一个关键字[implicit], 这个关键字会触发隐式转换, 这种转换是自动执行的, 将一种类型转换为另一种类型
7.1 隐式转换的作用
object ImplicitDemo {
def main(args: Array[String]): Unit = {
//定义一个变量Int类型进行赋值, 赋值的数据是3.5会报错
//创建隐式转换函数(方法)
//这个隐式转换方法不需要手动调用, 自动执行, 当代码执行后会进行全局扫描
//若当前作用域内有隐式转换函数会自动调用
implicit def m(d: Double) = d.toInt
val i: Int = 3.5
println(i)
}
}
7.2 使用引用转换对原有代码的增强
import java.io.File
import scala.io.Source
//隐式转换的作用
object ImplicitDemo {
def main(args: Array[String]): Unit = {
//提供隐式转换函数进行转换操作
implicit def fileToRichFile(from:File)=new RichFile(from)
val content: String = new File("Day04/Day04.iml").read
println(content)
}
}
//对系统File类进行扩展
class RichFile(val from:File){
//读取方法, 这个方法可以直接读取文件里的内容
//mkString相当于Java中的toString方法
def read=Source.fromFile(from.getPath).mkString
}
7.3 跨类使用隐式转换
class Timo {
val name="Time"
}
class Skill {
def caimogu(timo: Timo,skill:String)=println(timo.name+" "+skill)
}
//隐式转换所存在的类, 这个类最好是object, 这是一个不成文的规定
object Transform {
implicit def learn(t:Timo)= new Skill
}
//执行隐式转换的操作
object ImplicitTest {
def main(args: Array[String]): Unit = {
//若需要使用隐式转换, 当前因为不在一个作用域内, 所以需要导包
val timo = new Timo
import Transform.learn
timo.caimogu(timo,"test--"*5)
}
}
7.4 隐式转换的时机
- 当前方法中参数的类型与目标类型不一致时, 发生隐式转换
- 当前对象调用所在类中不存在的方法或成员的时候, 编译会自动将当前对象进行隐式转换