● scala中遍历数据结构
一、for循环
a. 以符号 <- 提供生成器
eg:
(1) //打印1、2、3
for (i <- 1 to 3) {
println(i)
}
(2) //遍历数组,添加到动态数组中
for(i <- 0 until data.length) {
dataArray.append(data(i)(0))
}
(3) //遍历tables数组,t作为每次遍历的元素在每次遍历时值分别是 "Runoob", "Baidu", "Google"
val browers = Array("Runoob", "Baidu", "Google")
for (t <- browers) {
if (free_browers.contains(t)) {
browerArrayBuffer.append(t)
}
}
b.提供多个生成器,并以分号分隔,if表达式作为控制守卫
eg:
(1) for (i <- 1 to 3; j <-1 to 3 if i > j) {
println(s"i=$i, j=$j, i+j=${i + j}")
}
输出: i=2, j=1, i+j=3
i=3, j=1, i+j=4
i=3, j=2, i+j=5
c.yield循环体
注:for循环中的 yield 会把当前的元素记下来,保存在集合中,循环结束后将返回该集合。Scala中for循环是有返回值的。如果被循环的是什么结构,返回就是什么,
,返回的变量的类型与循环表达式中的类型一致
eg:
(1) for (i <- 1 to 5) yield i * 2
输出:scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10)
(2) val filesHere = Map("java" -> 22, "scala" -> 6, "spark" -> 5)
val scalaFiles =for {
file <- filesHere
if file._1.startsWith("java")
if file._2 == 22
} yield file
println(scalaFiles)
输出:Map("java" -> 22)
注:循环次数为filesHere的size,file为每次遍历时的元素值
二、 foreach和map方法遍历数据结构
eg:
object arrayTest extends App{
var increase=(x:Int)=>x+1
val someNumbers = List ( -11, -10, - 5, 0, 5, 10)
var b = someNumbers.foreach(increase)
println(b.getClass)
var c = someNumbers.map(increase)
println(c.getClass)
c.foreach((x:Int)=>print(x+" "))
c.map((x:Int)=>print(x+" "))
}
注:Scala中的集合对象都有foreach和map两个方法。两个方法的共同点在于:都是用于遍历集合对象,并对每一项执行指定的方法。而两者的差异在于:foreach无返回值(准确说返回void),map返回 集合对象。见如下代码及运行结果:b.getClass 得到的是void, 而c.getClass得到的是colletion 。再看代码的第9-11行,foreach和map的运行结果一致。结论就是:foreach 无法代替map. 而 map方法却可以代替foreach。
注: scala中对集合使用map操作,即对集合的每一个元素都执行=>后面的转换操作,得到新的集合
eg: val arr2 = Array(1,2,3,4,5)
val ret = arr2.map(_ * 2)
ret值为Array(2,4,6,8,10)
引申-- flatten、flatMap、fillter、zip等函数组合器的使用
注: flatten可以把嵌套的结构展开.
eg: scala> List(List(1,2),List(3,4)).flatten
res0: List[Int] = List(1, 2, 3, 4)
注:flatMap是map的一种扩展,可以认为是flatten和map的结合,在flatMap中,我们会传入一个函数,该函数对每个输入都会返回一个集合(而不是一个元素),然后,flatMap把生成的多个集合“拍扁”成为一个集合。
eg: scala> val books = List("Hadoop","Hive","HDFS")
books: List[String] = List(Hadoop, Hive, HDFS)
scala> books flatMap (s => s.toList)
res0: List[Char] = List(H, a, o, o, p, H, i, v, e, H, D, F, S)
注:对象.方法在scala中可以写成 对象 空格 方法
注:filter用于滤除掉使函数返回false的元素
eg: scala> def isEven(i: Int): Boolean = i % 2 == 0
isEven: (i: Int)Boolean
scala> List(1,2,3,4).filter(isEven _)
res6: List[Int] = List(2, 4)
eg: //websocket根据用户向所有主题广播消息
body.topic.foreach(it => topicMap = topicMap + (it -> body.user))
topicMap.filter {
ele => val (k, v) = ele
v == s.user
}.get(s.topic).map(_ => send(s))
注: zip方法将两个集合结合在一起
eg: scala> List('a,'b,'c).zip(List(1,2,3))
res32: List[(Symbol, Int)] = List(('a,1), ('b,2), ('c,3))
注: zipWithIndex将元素和下标结合在一起
eg: scala> List(2,3,4,5).zipWithIndex
res33: List[(Int, Int)] = List((2,0), (3,1), (4,2), (5,3))
● scala的并发编程
一、 Future
注:Future提供了一套高效便捷的非阻塞并行操作管理方案。其基本思想很简单,所谓Future,指的是一类占位符对象,用于指代某些尚未完成的计算的结果。一般来说,由Future指代的计算都是并行执 行的,计算完毕后可另行获取相关计算结果。以这种方式组织并行任务,便可以写出高效、异步、非阻塞的并行代码。所谓Future,是一种用于指代某个尚未就绪的值的对象。而这个值,往往是某个 计算过程的结果:
(1)若该计算过程尚未完成,我们就说该Future未就位;
(2)若该计算过程正常结束,或中途抛出异常,我们就说该Future已就位。
Future的就位分为两种情况:
(1)当Future带着某个值就位时,我们就说该Future携带计算结果成功就位。
(2)当Future因对应计算过程抛出异常而就绪,我们就说这个Future因该异常而失败。
注: Future的一个重要属性在于它只能被赋值一次。一旦给定了某个值或某个异常,future对象就变成了不可变对象——无法再被改写。
a、创建Future
创建future对象最简单的方法是调用future方法,该future方法启用异步(asynchronous)计算并返回保存有计算结果的futrue,一旦该future对象计算完成,其结果就变的可用。
eg: import scala.concurrent._ import ExecutionContext.Implicits.global val session = socialNetwork.createSessionFor("user", credentials) val f: Future[List[Friend]] = Future { session.getFriends() }
在上述例子中,然后我们初始化一个session变量来用作向服务器发送请求,用一个假想的 createSessionFor 方法来返回一个List[Friend]。为了获得朋友列表,我们必须通过网络发送一个请求,这个请求 能耗时很长。这能从调用getFriends方法得到解释。为了更好的利用CPU,响应到达前不应该阻塞(block)程序的其他部分执行,于是在计算中使用异步。future方法就是这样做的,它并行地执行指定的 计算块,在这个例子中是向服务器发送请求和等待响应。一旦服务器响应,future f 中的好友列表将变得可用
b、 回调函数(Callbacks)
Scala的Future提供了回调函数来获取它的结果,当Future完成后会自动调用onComplete,我们只需要根据它的结果再做处理即可,而其他互不依赖的操作可以继续执行不会阻塞
eg: val fut = Future { Thread.sleep(1000) 1 + 1 } fut onComplete { case Success(r) => println(s"the result is ${r}") case _ => println("some Exception") } println("I am working") Thread.sleep(2000)
c 、 组合器(combinators)
//使用map操作从每个 future 中提取单一结果值,消除了所需的 match
/ case
结构
eg: val rateQuote = Future {
connection.getCurrentValue(USD)
}
val purchase = rateQuote map { quote =>
if (isProfitable(quote)) connection.buy(amount, quote)
else throw new Exception("not profitable")
}
purchase onSuccess {
case _ => println("Purchased " + amount + " USD")
}
二、 Actor
Scala中的Actor能够实现并行编程的强大功能,他是基于事件模板的并发机制。Actor是运用消息的发送接、收来实现多线程的。使用Scala能够更容易地实现多线程应用的开发,Actor是一种不共享数据,依赖于消息传递的一种并发编程模式,避免了死锁、资源争夺等情况。在具体实现的过程中,Scala中的Actor会不断的循环自己的邮箱,并通过receive偏函数进行消息的模式匹配并进行相应的处理如果Actor A和 Actor B要相互沟通的话,首先A要给B传递一个消息,B会有一个收件箱,然后B会不断的循环自己的收件箱, 若看见A发过来的消息,B就会解析A的消息并执行,处理完之后就有可能将处理的结果通过邮件的方式发送给A
a.创建actor
eg:
//继承Actor特质类
class HiActor extends Actor {
//重写Actor特质类的抽象方法act,从而实现自己的业务逻辑
override def act(): Unit = {
//消息循环
while (true)
//使用case模式匹配接收到的消息
receive {
case "Hi" => println("Hello")
}
}
}
//也可以用调用ActorSystem.actorOf(..)的方法创建actor
b.启动actor
val newActor = new HiActor
newActor.start()
c.发送消息
eg:
import scala.actors.Actor
//样例类
case class Person(val name:String,val age:Int)
//继承Actor特质类
class ActorTest extends Actor{
override def act(): Unit = {
while(true){
receive{
case Person(name,age)=>println(s"the name is ${name},the age is ${age}")
}
}
}
}
object Actor2 {
def main(args: Array[String]): Unit = {
val act=new ActorTest()
act.start()
act ! Person("张三",18)
act ! Person("李四",19)
act ! Person("赵六",16)
}
}
结果:
the name is 张三,the age is 18
the name is 李四,the age is 19
the name is 赵六,the age is 16
注:actor中!是发送消息异步执行,!?是同步执行。ask 函数就是 ?,意味着处理 message 的 actor (receiver) 需要把处理结果返回 sender。 tell 函数就是 !,不需返回
c. actor生命周期
actor是一个循环,actor在如下情形之一会终止执行:
(1)Act方法返回
(2)Act方法由于异常被终止(发送毒药信息)
(3)Actor调用exit方法
d. actor共享线程
某些程序包含的actor过多,以至于要为actor创建单独的线程开销会很大。此时需要考虑在同一个线程中运行多个actor。Actor有时大部分时间用户等待消息,这时actor所在的单独线程会堵塞,与其这样不如用一个线程来执行多个actor的消息处理函数。在Scala中,react方法可以实现这样的功能。react方法接收一个偏函数,并将它添加到邮箱,然后退出。
react工作原理
当你调用一个actor的start时,start方法会以某种方式来确保最终会有某个线程来调用那个actor的act方法。如果act方法调用了react ,则react方法会在actor的邮箱中查找传递给偏函数的能够处理的消息 。(和receive方法一样,传递待处理消息给偏函数的isDefinedAt方法。) 如果找到一个可以处理的消息,react 会安排一个在未来某个时间处理该消息的计划并抛出异常。如果它没有找到这样的消息,它会将actor置于“冷存储” 状态 ,在它通过邮箱收到更多消息时重新激活,并抛出异常。不论是哪种情况,react都会以这个异常的方式完成其执行,act 方法也随之结束 调用act的线程会捕获这个异常,忘掉这个actor , 并继续处理其他事务。这就是为什么你想要react在处理第一个消息之外做更多的事,你将需要在偏函数中再次调用act方法 ,或使用某种其他的手段来让react再次被调用。
eg:
import scala.actors.Actor
/**
* scala Actor构建在java的线程基础之上的,
* 为了避免频繁的线程创建、销毁和切换等,scala中提供了react方法
* 方法执行完毕后,仍然被保留
*/
//样例类
case class Persion(val name:String,val age:Int)
case class Animal(val name:String,val age:Int)
class MyReact extends Actor{
override def act(): Unit = {
loop{
react{
case Person(name,age)=>{
println(s"这是个人the name is ${name},the age is ${age}")
Thread.sleep(2000)
println(s"${name}会敲代码")
}
case Animal(name,age)=>{
println(s"这是动物the name is ${name},the age is ${age}")
Thread.sleep(2000)
println(s"${name}会摇尾巴")
}
case _ =>println("不清楚是啥")
}
}
}
}
object reactTest{
def main(args: Array[String]): Unit = {
val reac=new MyReact()
reac.start()
reac ! Person("蔡文姬",18)
reac ! Animal("梦奇",16)
println("完毕!")
}
}