Play For Scala 开发指南 - 第3章 常用类介绍

原创
2017/10/16 20:07
阅读数 135
转载请注明joymufeng,欢迎访问 PlayScala社区(http://www.playscala.cn)

原文链接:http://www.playscala.cn/doc/catalog?_id=j1_21

3.1 String

在Scala中,String更加方便好用:

//原始字符串一对三引号"""括起来,可包含多行字符串,内容不需要转义
"""Welcome here.
   Type "HELP" for help!"""
   
//类型转换
"100.0".toDouble

//判断字符串相等直接用"==",而不需要使用equals方法
val s1 = new String("a")
s1 == "a" // true

//字符串去重
"aabbcc".distinct // "abc"

//取前n个字符,如果n大于字符串长度返回原字符串
"abcd".take(10) // "abcd"

//字符串排序
"bcad".sorted // "abcd"

//过滤特定字符
"bcad".filter(_ != 'a') // "bcd"

//字符串插值, 以s开头的字符串内部可以直接插入变量,方便字符串构造
val i = 100
s"i=${i}" // "i=100"

Scala中没有受检异常(checked exception),所以你没有必要声明受检异常,如果真的发生异常,则会在运行时抛出。

3.2 Option

Scala用Option类型表示一个值是否存在,用来避免Java的NullPointerException。它有两个子类:Some和None。Some类型表示值存在,None类型则表示值不存在。 常用操作:

val opt: Option[String] = Some("hello")
//判断是否为None
opt.isEmpty // false
//如果为None,则返回默认值"default",否则返回opt持有的值
opt.getOrElse("default") 
//如果为None则返回"DEFAULT",否则将字符转为大写
opt.fold("DEFAULT"){ value => value.toUpperCase } // "HELLO"
//功能同上
opt match {
  case Some(v) => v.toUpperCase
  case None => "DEFAULT"
}

3.3 List

在Scala中,List要么是Nil(空列表),要么就是由head和tail组成的递归结构。 head是首元素,tail是剩下的List。所以你可以这样构建List:

val list = 1 :: Nil // 等价于:val list = List(1)

连续的两个冒号"::"就像是胶水,将List的head和tail粘在一起。 常用操作:

val list = List(1, 3, 2)
//获取第1个元素
list.headOption.getOrElse(0) // 1
//查找
list.find(_ % 2 == 0).getOrElse(0) // 2
//过滤
list.filter(_ % 2 == 1) // List(1, 3)
//排序
list.sorted // List(1, 2, 3)
//最小值/最大值/求和
list.min // 1
list.max // 3
list.sum // 6
//转化成字符串
list.mkString(",") // "1, 3, 2"

Scala提供的List基本可以实现SQL查询的所有功能,这也是Spark为什么基于Scala开发的原因。更多功能请参考官方文档

在Scala中默认的集合类例如List,Set,Map,Tuple等都是不可变的,所以调用其修改方法会返回一个新的实例。如果要使用可变集合,请使用scala.collection.mutable包下相应的类。不可变类型在编写并发代码时很有用。

3.4 Tuple

Tuple(元组)Tuple可以容纳不同类型的元素,最简单的形态是二元组,即由两个元素构成的Tuple, 可以使用_1, _2等方法访问其元素:

val t = ("a", 1) // 等价于:val t: Tuple2[String, Int] = ("a", 1)
t._1 // "a"
t._2 // 1

也可以使用模式匹配利用Tuple同时初始化一组变量:

val t = ("a", 1)
val (v1, v2) = t
v1 // "a"
v2 // 1

3.5 Map

Map其实是二元组的集合:

val map = Map("a" -> 1, "b" -> 2)

"->"其实是String类型上的方法,返回一个二元组:

"a" -> 1 //等价于: ("a", 1)

所以你也可以这样构建Map:

val map = Map(("a", 1), ("b", 2))

常用操作:

val map = Map("a" -> 1, "b" -> 2)
//读取
map("a") // 1
//写入或添加键值
map("a") = 0
//删除键值
map - "a" // Map(b -> 2)

3.6 Future

Future和Promise是Scala提供的最吸引人的特性之一,借助Future和Promise你可以轻松地编写完全异步非阻塞的代码,这在多处理器时代显得格外重要。

Future用于获取异步任务的返回结果。Future有两种状态:完成(completed)和未完成(not completed)。处于完成状态的Future可能包含两种情况的信息,一种是异步任务执行成功了,Future中包含异步任务执行成功的返回结果;另一种是异步任务执行失败了,Future中包含了相应的Exception信息。Future的独特之处在于它的值只能被写入一次,之后就会变为一个不可变值,其中包含成功或失败信息。你可以在Future上注册一个回调函数,以便在任务执行完成后得到通知:

import scala.concurrent.ExecutionContext.Implicits.global
val f = Future{ 1 + 2 }
f.onComplete{ t =>
  t match{
    case Success(v) => println("success: " + v)
    case Failure(t) => println("failed: " + t.getMessage)
  }
}
//等待任务结束
Await.ready(f, 10 seconds)

onComplete方法接受一个一元函数,类型为:Try[T] => U。Try类型和Option类型很像,也有两个子类SuccessFailure,前者表示任务执行成功,后者表示任务执行失败。

第1行import语句导入了一个隐式的ExecutionContext,你可以把它理解成是一个线程池,Future类在需要时会自动使用其上的线程。在Scala中你不需要直接和线程打交道。

由于Future也是一个容器类,所以可以使用for语句取回它的值:

val f = Future{ 1 + 2 }
for(v <- f) {
    println(v) // 3
}

也可以使用map方法对任务结果进行转换:

val f1 = Future{ 1 + 2 }
val f2 = f1.map(v => v % 2)
for(v <- f2) {
    println(v) // 1
}

利用for语句可以等待多个Future的返回结果:

val f1 = Future{ 1 + 2 }
val f2 = Future{ 3 + 4 }
for{
    v1 <- f1
    v2 <- f2
} {
    println(v1 + v2) // 10
}

结合yield可以返回一个新的Future:

val f1 = Future{ 1 + 2 }
val f2 = Future{ 3 + 4 }
val f3 =  
    for {
        v1 <- f1
        v2 <- f2
    } yield {
        v1 + v2
    }

3.7 Promise

有时我们需要精细地控制Future的完成时机和返回结果,也就是说我们需要一个控制Future的开关,没错,这个开关就是Promise。每个Promise实例都会有一个唯一的Future与之相关联:

val p = Promise[Int]()
val f = p.future
for (v <- f) { println(v) }

//3秒钟之后返回3
Thread.sleep(3000)
p.success(3)

//等待任务结束
Await.ready(f, 10 seconds)
展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部