跳出循环语句的3种方法
方法一:使用boolean控制变量
while循环:
var flag = true
var res = 0
var n = 0
while(flag) {
res += n
n += 1
if (n == 5) {
flag = false
}
}
for循环:(高级for循环,加上了if守卫)
var flag = true
var res = 0
for (i <- 0 until 10 if flag) {
res += i
if (i == 4) flag = false
}
方法二:在嵌套函数中使用return
def add_outer() = {
var res = 0
def add_inner() {
for (i <- 0 until 10)
{
if (i == 5)
{
return
}
res += i
}
}
add_inner()
res
}
方法三:使用Breaks对象的break方法
跟java里面的break比较类似,相对来说,比较灵活好用;与breakable代码块配合使用
import scala.util.control.Breaks._
var res = 0
breakable {
for (i <- 0 until 10) {
if (i == 5) {
break;
}
res += i
}
}
多维数组
什么是多维数组?:数组的元素,还是数组,数组套数组,就是多维数组
构造指定行与列的二维数组:Array.ofDim方法
val multiDimArr1 = Array.ofDim[Double](3,
4)
multiDimArr1(0)(0) = 1.0
构造不规则多维数组:
val multiDimArr2 = new Array[Array[Int]](3)
multiDimArr2(0) = new Array[Int] (1)
multiDimArr2(1) = new Array[Int] (2)
multiDimArr2(2) = new Array[Int] (3)
multiDimArr2(1)(1) = 1
Java数组与Scala数组缓冲的隐式转换
Scala代码中,直接调用JDK(Java)的API,比如调用一个Java类的方法,势必可能会传入Java类型的list,此时如果直接把Scala的ArrayBuffer传入Java接收ArrayList的方法,肯定不行。这可以先将Scala中的Buffer转换为Java中的List即可
import
scala.collection.JavaConversions.bufferAsJavaList
import scala.collection.mutable.ArrayBuffer
val command =
ArrayBuffer("javac",
"C:\\Users\\Administrator\\Desktop\\HelloWorld.java") // 调用操作系统命令编译源码
// ProcessBuilder为JDK中的类,构造函数为:ProcessBuilder(List<String>
command),要求的是List,所以需要将Scala的Buffer转换为Java中的List,才能在Java中使用
val processBuilder = new
ProcessBuilder(command)
val process = processBuilder.start()
val res = process.waitFor()
下面是将Java返回的List转换为Buffer:
import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.mutable.Buffer
// ProcessBuilder的command()方法返回的是List<String>,所以需要将List<String>隐式转换为Buffer[String],才能在Scala中使用
val cmd: Buffer[String] =
processBuilder.command()
Java Map与Scala
Map的隐式转换
import
scala.collection.JavaConversions.mapAsScalaMap
val javaScores = new java.util.HashMap[String, Int]()
javaScores.put("Alice", 10)
javaScores.put("Bob", 3)
javaScores.put("Cindy", 8)
val scalaScores: scala.collection.mutable.Map[String,
Int] = javaScores // Java Map自动隐式转换 Scala Map
import
scala.collection.JavaConversions.mapAsJavaMap
import java.awt.font.TextAttribute._
val scalaAttrMap = Map(FAMILY ->
"Serif", SIZE -> 12)
val font = new java.awt.Font(scalaAttrMap)
// Scala Map自动隐式转换
Java Map
Tuple拉链操作
Tuple拉链操作指的就是zip操作
zip操作,是Array类的方法,用于将两个Array,合并为一个Array
比如Array(v1)和Array(v2),使用zip操作合并后的格式为Array((v1,v2))
合并后的Array的元素类型为Tuple
val students = Array("Leo",
"Jack", "Jen")
val scores = Array(80, 100, 90)
val studentScores = students.zip(scores)
for ((student, score) <- studentScores)
println(student + " " +
score)
如果Array的元素类型是个Tuple,调用Array的toMap方法,可以将Array转换为Map
studentScores.toMap
内部类的作用域:外部类对象
import scala.collection.mutable.ArrayBuffer
class Class {
class Student(val name: String)
val students = new
ArrayBuffer[Student]
def register(name: String) = {
new Student(name)
}
}
val c1 = new Class
val leo = c1.register("leo")
c1.students += leo
val c2 = new Class
val jack = c2.register("jack")
c1.students += jack // error: type mismatch;
扩大内部类作用域:伴生对象
object Class {
class Student(val name: String)
}
class Class {
val students = new ArrayBuffer[Class.Student]
def register(name: String) = {
new Class.Student(name)
}
}
val c1 = new Class
val leo = c1.register("leo")
c1.students += leo
val c2 = new Class
val jack = c2.register("jack")
c1.students += jack
扩大内部类作用域:类型投影
class Class {
class Student(val name: String)
val students = new ArrayBuffer[Class#Student] // 明确说明使用的是Class类型中的Student类,而非Class对象中的
def register(name: String) = {
new Student(name)
}
}
val c1 = new Class
val leo = c1.register("leo")
c1.students += leo
val c2 = new Class
val jack = c2.register("jack")
c1.students += jack
内部类获取外部类的引用
class Class(val name: String) {
outer =>
//名随便
class
Student(val name: String) {
def introduceMyself =
"Hello, I'm " + name + ", I'm very happy to join class " + outer.name
}
def register(name: String) = {
new Student(name)
}
}
val c1 = new Class("c1")
val leo = c1.register("leo")
leo.introduceMyself
package定义
因为要对多个同名的类进行命名空间的管理,避免同名类发生冲突
比如说,scala.collection.mutable.Map和scala.collection.immutable.Map
package定义的第一种方式: 多层级package定义(比较差的做法,一般不这么干)
package com {
package
sn {
package
scala {
class
Test {}
}
}
}
package定义的第二种方式: 串联式package定义(也不怎么样,一般也不这么干)
package com.sn.scala {
package
service {
class
Test {}
}
}
package定义的第三种方式: 文件顶部package定义
package com.sn.scala.service
class Test {
}
package特性
同一个包定义,可以在不同的scala源文件中的:
Test1.scala
package com {
package sn {
package scala {
class Test1
}
}
}
Test2.scala
package com {
package sn {
package scala {
class Test2
}
}
}
一个scala源文件内,可以包含两个包:
Test3.scala
package com {
package sn {
package scala1 {
class Test
}
}
}
package com {
package sn {
package scala2 {
class Test
}
}
}
子包中的类,可以访问父包中的类:
Test.scala
package com {
package sn {
package scala {
object Utils {
def isNotEmpty(str:
String): Boolean = str != null && str != ""
}
class Test
package service {
class MyService {
def sayHello(name:
String) {
if
(Utils.isNotEmpty(name)) {
println("Hello, " + name)
} else {
println("Who are you?")
}
}
}
}
}
}
}
object T {
def main(args:
Array[String]) {
import
com.sn.scala.service._
new
MyService().sayHello("")
new
MyService().sayHello("leo")
}
}
相对包名与绝对包名:
package com {
package sn {
package scala {
object Utils {
def isNotEmpty(str:
String): Boolean = str != null && str != ""
}
class Test
package collection {}
package service {
class MyService {
//
报错,默认使用相对报名,从com.sn.scala.collection包中,寻找mutable包下的ArrayBuffer类
//
但是找不到,所以会报错
//
val names = new scala.collection.mutable.ArrayBuffer[String]
//
正确的做法是使用_root_,引用绝对包名
val names = new _root_.scala.collection.mutable.ArrayBuffer[String]
def sayHello(name:
String) {
if
(Utils.isNotEmpty(name)) {
println("Hello, " + name)
} else {
println("Who are you?")
}
}
}
}
}
}
}
定义package对象(比较少用):
package内的成员,可以直接访问package对象内的成员
package com.sn.scala
package object service {
val defaultName = "Somebody"
}
package service {
class MyService {
def sayHello(name:
String) {
if (name != null && name
!= "") {
println("Hello, " + name)
} else {
println("Hello, " + defaultName)//访问包对象中的成员
}
}
}
}
package可见性:
package com.sn {
package scala {
class Person {
//com.sn.scala包下可见
private[scala] val name = "leo"
//com.sn包下可见
private[sn] val age = 25
}
object T1 {
new Person().name
}
}
object T2 {
import
com.sn.scala.Person
new Person().age
}
}
import
package com.sn.scala
package service {
class MyService {
def sayHello(name:
String) {}
}
}
import
com.sn.scala.service.MyService;
object MainClass {
def main(args:
Array[String]): Unit = {
val service = new MyService
}
}
import特性一: 用import
com.sn.scala.service._这种格式,可以导入包下所有的成员
import特性二: scala与java不同之处在于,任何地方都可以使用import,比如类内、方法内,这种方式的好处在于,可以在一定作用域范围内使用导入
object MainClass {
def main(args:
Array[String]): Unit = {
import
com.sn.scala.service._
val service = new MyService
}
}
import特性三: 选择器、重命名、隐藏
import com.sn.scala.service.{ MyService },仅仅导入com.sn.scala.service包下的MyService类,其它不导入
import com.sn.scala.service.{ MyService
=> MyServiceImpl },将导入的类进行重命名
import com.sn.scala.service.{ MyService
=> _, _ },导入com.sn.scala.service包下所有的类,但是隐藏掉MyService类
import特性四: 隐式导入
每个scala程序默认都会隐式导入以下几个包下所有的成员
import java.lang._
import scala._
import Predef._
重写field的提前定义
默认情况下,如果父类中的构造函数代码用到了被子类重写(或实现)的field,那么可能会出现未被正确初始化的问题:
当父类的构造函数执行时,如果使用到了被子类实现或重写过的field(field重写或实现相当于对应的getter方法被重写),会调用子类重写或实现过的field,由于子类构造器还没有执行,所以会返回子类中被重写或实现过的该field的初始值(比如Int为0,String为null)。详细可以参数《Scala编程基础》中的“trait field的初始化”相应章节
class Student {
val classNumber: Int = 10
val classScores: Array[Int] = new Array[Int](classNumber) // 会调用子类被重写过的classNumber字段
}
class PEStudent extends Student {
override val classNumber: Int = 3
}
scala> new PEStudent().classScores
res42: Array[Int] = Array()
本来我们期望的是,PEStudent,可以从Student继承来一个长度为3的classScores数组,结果PEStudent对象,只有一个长度为0的classScores数组
此时只能使用Scala对象继承的一个高级特性: 提前定义,在父类构造函数执行之前,先执行子类的构造函数中的某些代码
class Student {
val classNumber: Int = 10
val classScores: Array[Int] = new Array[Int](classNumber) // 会调用子类被重写过的classNumber字段
}
class PEStudent extends {
override val classNumber: Int = 3
} with Student
scala> new
PEStudent().classScores
res43: Array[Int]
= Array(0, 0, 0)
也可以这样,但Student需定义成trait:
trait Student {
val classNumber: Int = 10
val classScores: Array[Int] = new Array[Int](classNumber)
}
class PEStudent extends Student {
}
var ps = new {override val classNumber:
Int = 3} with PEStudent with Student
ps.classScores
Scala的继承层级
这里我们大概知道一下Scala的继承层级,我们写的所有的Scala trait和class,都是默认继承自一些Scala根类的,有一些基础的方法
Scala中,最顶端的两个trait是Nothing和Null,Null trait唯一的对象就是null
其次是继承了Nothing
trait的Any类
接着Anyval
trait和AnyRef类,都继承自Any类
Any类是个比较重要的类,其中定义了isInstanceOf和asInstanceOf等方法,以及equals、hashCode等对象的基本方法
Any类,有点像Java中的Object基类
AnyRef类,增加了一些多线程的方法,比如wait、notify/notifyAll、synchronized等,也是属于Java Object类的一部分
对象相等性
这里,我们要知道,在scala中,你如何判断两个引用变量,是否指向同一个对象实例
AnyRef的eq方法用于检查两个变量是否指向同一个对象实例
AnyRef的equals方法默认调用eq方法实现,也就是说,默认情况下,判断两个变量相等,要求必须指向同一个对象实例
通常情况下,自己可以重写equals方法,根据类的fields来判定是否相等
此外,定义equals方法时,也最好使用同样的fields,重写hashCode方法
如果只是想要简单地通过是否指向同一个对象实例,判定变量是否相当,那么直接使用==操作符即可,默认判断null,然后调用equals方法
class Product(val name: String, val price: Double) {
final override def equals(other:
Any) = {
val that =
other.asInstanceOf[Product]
if (that == null) false
else name == that.name && price == that.price
}
final override def hashCode = 13 * name.hashCode + 17 * price.hashCode
}
文件操作
必须导入scala.io.Source类: import scala.io.Source
方法一: 使用Source.getLines返回的迭代器
val source =
Source.fromFile("C://Users//Administrator//Desktop//test.txt", "UTF-8")
val lineIterator =
source.getLines
for (line <-
lineIterator) println(line)
方法二: 将Source.getLines返回的迭代器转换成数组
val source =
Source.fromFile("C://Users//Administrator//Desktop//test.txt", "UTF-8")
val lines =
source.getLines.toArray
for(line <- lines)
println(line)
这里说明一点:
一个BufferedSource对象的getLines方法,只能调用一次,一次调用完之后,遍历了迭代器里所有的内容,就已经把文件里的内容读取完了
如果反复调用source.getLines,是获取不到内容的。此时,必须重新创建一个BufferedSource对象
方法三: 调用Source.mkString,返回文本中所有的内容
val source =
Source.fromFile("C://Users//Administrator//Desktop//test.txt", "UTF-8")
val lines =
source.mkString
使用完BufferedSource对象之后,调用BufferedSource.close方法,关闭IO流资源
遍历一个文件中的每一个字符
val source =
Source.fromFile("C://Users//Administrator//Desktop//test.txt", "UTF-8")
for (c <- source)
print(c)// BufferedSource,也实现了一个Iterator[Char]的一个trait
从URL以及字符串中读取字符
val source =
Source.fromURL("http://www.baidu.com", "UTF-8")
val source = Source.fromString("Hello
World")
结合Java
IO流,读取任意文件
案例: 结合java IO流,做一个文件拷贝的案例
import java.io._
val f = new File("C:/Users/Administrator/Desktop/1.txt")
val fis = new
FileInputStream(f)
val fos = new
FileOutputStream(new File("C:/Users/Administrator/Desktop/2.txt"))
val buf = new Array[Byte](f.length.toInt)
fis.read(buf)
fos.write(buf)
fis.close()
fos.close()
结合Java
IO流,写文件
val pw = new PrintWriter("C://Users//Administrator//Desktop//test4.txt")
pw.println("Hello
World")
pw.close()
递归遍历指定目录的所有子目录:
import java.io._
def
getSubdirIterator(dir: File): Iterator[File] = {
//filter:对数组进行过滤,只要目录的。filter返回的为数组
val childDirs =
dir.listFiles.filter(_.isDirectory)
//childDirs.toIterator:数组转迭代器。++:两个迭代器相加
childDirs.toIterator ++ childDirs.toIterator.flatMap(getSubdirIterator
_)//对子目录进行递归
}
val iterator = getSubdirIterator(new File("C://Users//Administrator//Desktop"))
for (d <- iterator) println(d)
序列化以及反序列化
还是要借助于Java序列化和反序列化机制
如果要序列化,那么就必须让类,有一个@SerialVersionUID,定义一个版本号
要让类继承一个Serializable
trait
@SerialVersionUID(42L) class Person(val name: String) extends Serializable
val leo = new Person("leo")
import java.io._
val oos = new
ObjectOutputStream(new FileOutputStream("C://Users//Administrator//Desktop//test.obj"))
oos.writeObject(leo)
oos.close()
val ois = new
ObjectInputStream(new FileInputStream("C://Users//Administrator//Desktop//test.obj"))
val restoredLeo = ois.readObject().asInstanceOf[Person]
restoredLeo.name
偏函数
偏函数,是一种高级的函数形式
简单来说,偏函数是什么,其实就是没有定义好明确的输入参数的函数(或是不能处理所有情况的函数),函数体就是一连串的case语句
一般的函数:
def getStudentGrade(name: String) = {
...
}
偏函数是PartialFunction[A,
B]类的一个实例
这个类有两个方法,一个是apply()方法,直接调用可以通过函数体内的case进行匹配,返回结果;
另一个是isDefinedAt()方法,可以返回一个输入,是否跟任何一个case语句匹配
学生成绩查询案例:
val getStudentGrade:
PartialFunction[String, Int] = {
case
"Leo" => 90; case "Jack" => 85; case
"Marry" => 95
}
getStudentGrade("Leo")
getStudentGrade.isDefinedAt("Tom")
执行外部命令
import sys.process._
"javac HelloWorld.java" !
"java HelloWorld" !
正则表达式支持
定义一个正则表达式,使用String类的r方法
此时返回的类型是scala.util.matching.Regex类的对象
val pattern1 = "[a-z]+".r
val str = "hello 123 world 456"
获取一个字符串中,匹配正则表达式的部分,使用findAllIn,会获取到一个Iterator,迭代器
然后就可以去遍历各个匹配正则的部分,去进行处理
for (matchString <-
pattern1.findAllIn(str)) println(matchString)
同理,使用findFirstIn,可以获取第一个匹配正则表达式的部分
pattern1.findFirstIn(str)
使用replaceAllIn,可以将匹配正则的部分,替换掉
pattern1.replaceFirstIn("hello
world", "replacement")
使用replaceFirstIn,可以将第一个匹配正则的部分,替换掉
pattern1.replaceAllIn("hello
world", "replacement")
提取器
apply方法:
伴生类和伴生对象的概念,companion class和companion object
伴生对象里面,可以定义一个apply方法
直接调用类(参数),方式,就相当于在调用apply方法
此时在apply方法中,通常来说(也不一定),会创建一个伴生类的对象,返回回去
这种方式,有一个好处,创建对象呢,非常的方便
不要每次都是new
类(参数),而是 类(参数)
提取器 unapply方法:
和apply方法,顾名思义,那就是反过来
apply方法,可以理解为,接收一堆参数,然后返回一个对象
unapply方法,可以理解为,接收一个字符串,解析成一个对象的各个字段
提取器就是一个包含了unapply方法的对象,跟apply方法正好相反
apply方法,是接收一堆参数,然后构造出来一个对象
unapply方法,是接收一个字符串,然后解析出对象的属性值
class Person(val name: String, val age:
Int)
object Person {
def apply(name: String, age: Int) =
new Person(name, age)
def unapply(str: String)
= {
val splitIndex = str.indexOf(" ")
if (splitIndex == -1) None
else Some((str.substring(0, splitIndex), str.substring(splitIndex + 1)))
}
}
scala> val Person(name, age) = "leo 25"// 会调用unapply方法进行解析,将"leo 25"解析成相应成员
name: String = leo
age: String = 25
样例类的提取器
scala中的样例类,类似于java中的javabean
java中的JavaBean,包含了一堆属性,field; 每个field都有一对getter和setter方法:
public class Person {
private
String name;
private
int age;
public
String getName() {
return
name;
}
public
void setName(String name) {
this.name
= name;
}
public
int getAge() {
return
age;
}
public
void setAge(int age) {
this.age
= age;
}
}
scala中的样例类,默认就是提供apply方法和unapply方法的
case class Person(name: String, age: Int)
val p = Person("leo", 25)
p match {
case
Person(name, age) => println(name + ":
" + age)
}
只有一个参数的提取器
之前,已经跟大家讲解过普通的提取器
相当于是,比如说,接收一个字符串,作为参数
然后从字符串里面解析出来多个字段值,然后将多个字段值封装在一个tuple中
作为Some类型的对象,返回
如果你的类只有一个字段,字符串里面只有一个字段,
解析出来的一个字段,是没有办法放在tuple中的,因为scala中的tuple,规定了,必须要两个以及两个以上的值
这个时候,在提取器,unapply方法中,只能将一个字段值,封装在Some对象中,直接返回
class Person(val name: String)
object Person {
def unapply(input: String):
Option[String] = Some(input)
}
val Person(name) = "leo"
注解
代码中,加入一些特殊的标记。在代码编译或运行时,在碰到注解的时候,做一些特殊的操作
scala中,可以给类、方法、field、local
variable、constructor
/ method / function parameter添加注解
而且scala是支持给某个目标添加多个注解的
这里有一些特例:如果要给类的主构造函数添加注解,那么需要在构造函数前添加注解,并加上一对圆括号
比如说
class Person @Unchecked() (val name:
String, val age: Int)
还可以给表达式添加注解,此时需要在表达式后面加上冒号以及注解,比如
val scores = Map("Leo" -> 90,
"Jack" -> 60)
(scores.get("Leo"): @unchecked)
match { case score => println(score) }
除此之外,还可以给类型参数和变量的类型定义添加注解
要自己动手开发一个注解,就必须扩展Annotation trait,比如
class Test extends annotation.Annotation
@Test
class myTest
注解中,是可以有参数的,比如
class Test(var timeout: Int) extends annotation.Annotation
@Test(timeout = 100) class myTest
如果注解只有一个参数的话,那么也可以不用指定注解的参数名,比如
@Test(100) class myTest
常用注解
@volatile var name = "leo" 轻量级的java多线程并发安全控制
jvm,java虚拟机中,可以有多个线程
每个线程都有自己的工作区,还有一块儿所有线程共享的工作区
每次一个线程拿到一个公共的变量,都需要从共享区中拷贝一个副本到自己的工作区中使用和修改
然后修改完以后,再在一个合适的时机,将副本的值,写回到共享区中
这里就会出现一个多线程并发访问安全的问题
多个线程如果同时拷贝了变量副本,都做了不同的修改
然后依次将副本修改的值,写回到共享区中,会依次覆盖掉之前的一些副本值
就会出现变量的值,是不符合预期的
volatile关键字修饰的变量
它可以保证,一个线程在从共享区获取一个变量的副本时,都会强制刷新一下这个变量的值
保证自己获取到的变量的副本值是最新的
所以这样子做呢,是一种轻量级的多线程并发访问控制办法
但是也不是百分之百保险的,还是有可能会出现错误的风险
@transient var name =
"leo" 瞬态字段,不会序列化这个字段
之前讲序列化,默认会将一个对象中所有的字段的值,都序列化到磁盘文件中去
然后反序列化的时候,还可以获取这些字段的值
加了transient的字段,是瞬态的,序列化的时候,不会序列化这个字段
反序列化的时候,这个字段也就没有值了
@SerialVersionUID(value) 标记类的序列化版本号
序列化版本号,这个什么意思
如果我们将一个类的对象序列化到磁盘文件上了
结果过了一段时间以后,这个类在代码中改变了,此时如果你想将磁盘文件中的对象反序列化回来
就会报错,因为你的序列化的对象的结构与代码中的类结构已经不一样了
针对这种问题,就应该有一个序列化版本号
如果你的类改变了,就重新生成一个序列化版本号
反序列化的时候,就会发现序列化类型的版本号和代码中的类的版本号,不一样
@native 标注用c实现的本地方法
@throws(classOf[Exception]) def test() {} 给方法标记要抛出的checked异常
@varargs def test(args: String*) {} 标记方法接收的是变长参数
@BeanProperty 标记生成JavaBean风格的getter和setter方法
@BooleanBeanProperty 标记生成is风格的getter方法,用于boolean类型的field
@deprecated(message = "") 让编译器提示警告
@unchecked 让编译器不提示警告
xml
scala对xml有很好的支持,可以直接在scala代码中定义一个xml文档元素
scala> val books =
<books><book>my first scala book</book></books>
books: scala.xml.Elem
= <books><book>my first scala
book</book></books>
此时doc的类型是scala.xml.Elem,也就是一个xml元素
scala还可以直接定义多个同级别的xml元素
scala> val books = <book>my first
scala book</book><book>my first spark book</book>
books: scala.xml.NodeBuffer
= ArrayBuffer(<book>my first scala book</book>,
<book>my first spark book</book>)
此时doc的类型是scala.xml.NodeBuffer,也就是一个xml节点序列
XML节点类型
Node类是所有XML节点类型的父类型,两个重要的子类型是Text和Elem。
Elem表示一个XML元素,也就是一个XML节点。scala.xml.Elem类型的label属性,返回的是标签名,child属性,返回的是子元素。
scala.xml.NodeSeq类型,是一个元素序列,可以用for循环,直接遍历它。
scala> val books =
<books><book></book></books>
books: scala.xml.Elem =
<books><book></book></books>
scala> books.label
res10: String = books
可以通过scala.xml.NodeBuffer类型,来手动创建一个节点序列:
scala> val booksBuffer = new
scala.xml.NodeBuffer
booksBuffer: scala.xml.NodeBuffer =
ArrayBuffer()
scala> booksBuffer +=
<book>book1</book>
res2: booksBuffer.type = ArrayBuffer(<book>book1</book>)
scala> booksBuffer +=
<book>book2</book>
res3: booksBuffer.type =
ArrayBuffer(<book>book1</book>, <book>book2</book>)val
books: scala.xml.NodeSeq = booksBuffer
scala> for(bb <- booksBuffer)
println(bb)
<book>book1</book>
<book>book2</book>
scala> val books: scala.xml.NodeSeq =
booksBuffer
books: scala.xml.NodeSeq =
NodeSeq(<book>book1</book>, <book>book2</book>)
scala> for(b <- books) println(b)
<book>book1</book>
<book>book2</book>
xml元素的属性
scala.xml.Elem.attributes属性,可以返回这儿xml元素的属性,是Seq[scala.xml.Node]类型的,继续调用text属性,可以拿到属性的值
scala> val book = <book
id="1" price="10.0">book1</book>
book: scala.xml.Elem = <book
id="1" price="10.0">book1</book>
scala> val bookId =
book.attributes("id").text
bookId: String = 1
还可以遍历属性
scala> for(attr <- book.attributes)
println(attr)
id="1"
price="10.0"
price="10.0"
还可以调用book.attributes.asAttrMap,获取一个属性Map
在xml中嵌入scala代码
scala> val books =
Array("book1", "book2")
books: Array[String] = Array(book1, book2)
scala> <books><book>{
books(0) }</book><book>{ books(1) }</book></books>
res12: scala.xml.Elem =
<books><book>book1</book><book>book2</book></books>
scala> <books>{ for (book <-
books) yield <book>{book}</book> }</books>
res13: scala.xml.Elem =
<books><book>book1</book><book>book2</book></books>
还可以在xml属性中嵌入scala代码:
<book id={ books(0) }>{ books(0)
}</book>
修改xml元素
默认情况下,scala中的xml表达式是不可改变的;如果要修改xml元素的话,必须拷贝一份再修改
val books =
<books><book>book1</book></books>
添加一个子元素
val booksCopy = books.copy(child =
books.child ++ <book>book2</book>)
val book = <book
id="1">book1</book>
import scala.xml._
修改一个属性
val bookCopy = book % Attribute(null,
"id", "2", Null)
添加一个属性
val bookCopy = book % Attribute(null,
"id", "2", Attribute(null, "price",
"10.0", Null))
加载和写入外部xml文件
import scala.xml._
import java.io._
使用scala的XML类加载
val books =
XML.loadFile("C://Users//Administrator//Desktop//books.xml")
使用Java的FileInputStream类加载
val books = XML.load(new
FileInputStream("C://Users//Administrator//Desktop//books.xml"))
使用Java的InputStreamReader类指定加载编码
val books = XML.load(new
InputStreamReader(new
FileInputStream("C://Users//Administrator//Desktop//books.xml"),
"UTF-8"))
将内存中的xml对象,写入外部xml文档
XML.save("C://Users//Administrator//Desktop//books2.xml",
books)
集合元素操作符
col :+ ele 将元素添加到集合尾部 Seq
ele +: col 将元素添加到集合头部 Seq
col + ele 在集合尾部添加元素 Set、Map
col + (ele1, ele2) 将其他集合添加到集合的尾部 Set、Map
col - ele 将元素从集合中删除 Set、Map、ArrayBuffer
col - (ele1, ele2) 将子集合从集合中删除 Set、Map、ArrayBuffer
col1 ++ col2 将其他集合添加到集合尾部 Iterable
col2 ++: col1 将其他集合添加到集合头部 Iterable
ele :: list 将元素添加到list的头部 List
list2 ::: list1 将其他list添加到list的头部 List
list1 ::: list2 将其他list添加到list的尾部 List
set1 | set2 取两个set的并集 Set
set1 & set2 取两个set的交集 Set
set1 &~ set2 取两个set的diff Set
col += ele 给集合添加一个元素 可变集合
col += (ele1, ele2) 给集合添加一个集合 可变集合
col ++= col2 给集合添加一个集合 可变集合
col -= ele 从集合中删除一个元素 可变集合
col -= (ele1, ele2) 从集合中删除一个子集合 可变集合
col —= col2 从集合中删除一个子集合 可变集合
ele +=: col 向集合头部添加一个元素 ArrayBuffer
col2 ++=: col 向集合头部添加一个集合 ArrayBuffer
集合的常用方法
head、last、tail
length、isEmpty
sum、max、min
count、exists、filter、filterNot
takeWhile、dropWhile
take、drop、splitAt
takeRight、dropRight
sclie
contains、startsWith、endsWith
indexOf
intersect、diff
map操作,一对一映射
scala> val scoreMap =
Map("leo" -> 90, "jack" -> 60, "tom" ->
70)
scoreMap: scala.collection.immutable.Map[String,Int]
= Map(leo -> 90, jack -> 60, tom -> 70)
scala> val names = List("leo",
"jack", "tom")
names: List[String] = List(leo, jack, tom)
scala> names.map(scoreMap(_)) // 根据Key索引从Map中取对应的value
res14: List[Int] = List(90, 60, 70)
flatMap操作,一对多映射
scala> val scoreMap =
Map("leo" -> List(80, 90, 60), "jack" -> List(70, 90,
50), "tom" -> List(60,70,40))
scoreMap:
scala.collection.immutable.Map[String,List[Int]] = Map(leo -> List(80, 90,
60), jack -> List(70, 90, 50), tom -> List(60, 70, 40))
scala> names.map(scoreMap(_))
res15: List[List[Int]] = List(List(80, 90,
60), List(70, 90, 50), List(60, 70, 40))
scala> names.flatMap(scoreMap(_)) // 展平
res16: List[Int] = List(80, 90, 60, 70, 90,
50, 60, 70, 40)
collect操作,结合偏函数使用
scala> "abc".collect { case
'a' => 1; case 'b' => 2; case 'c' => 3 }
res17:
scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3)
foreach操作,遍历
names.foreach(println _)
reduce操作
scala> List(1, 2, 3,4).reduceLeft(_ - _)
// 1 - 2 - 3 - 4
res21: Int = -8
scala> List(1, 2, 3,4).reduceRight(_ -
_) // 1 - ( 2 - (3 - 4) )
res22: Int = 2
fold操作
scala> List(1, 2, 3,4).foldLeft(10)(_ -
_) // 10 - 1 - 2 - 3
res23: Int = 0
scala> List(1, 2, 3,4).foldRight(10)(_ -
_) // 1 - ( 2 - ( 3 - (4 - 10) ) )
res24: Int = 8
实际上,我们可以直接使用reduce,而不用reduceLeft,这时,默认采用的是reduceLeft,如下:
scala> List(1,2,3,4) .reduce(_ - _)
res5: Int = -8