Scala编程进阶

跳出循环语句的3种方法... 2

多维数组... 3

Java数组与Scala数组缓冲的隐式转换... 3

Java MapScala Map的隐式转换... 3

Tuple拉链操作... 4

内部类的作用域:外部类对象... 4

扩大内部类作用域:伴生对象... 4

扩大内部类作用域:类型投影... 5

内部类获取外部类的引用... 5

package定义... 5

package特性... 6

import. 9

重写field的提前定义... 10

Scala的继承层级... 11

对象相等性... 11

文件操作... 11

序列化以及反序列化... 13

偏函数... 13

执行外部命令... 14

正则表达式支持... 14

提取器... 14

样例类的提取器... 15

只有一个参数的提取器... 16

注解... 16

常用注解... 17

xml 18

XML节点类型... 18

xml元素的属性... 19

xml中嵌入scala代码... 19

修改xml元素... 19

加载和写入外部xml文件... 20

集合元素操作符... 20

集合的常用方法... 21

   

跳出循环语句的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代码中,直接调用JDKJava)的API,比如调用一个Java类的方法,势必可能会传入Java类型的list,此时如果直接把ScalaArrayBuffer传入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") // 调用操作系统命令编译源码

// ProcessBuilderJDK中的类,构造函数为:ProcessBuilder(List<String> command),要求的是List,所以需要将ScalaBuffer转换为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

// ProcessBuildercommand()方法返回的是List<String>,所以需要将List<String>隐式转换为Buffer[String],才能在Scala中使用

val cmd: Buffer[String] = processBuilder.command()

Java MapScala 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,调用ArraytoMap方法,可以将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.Mapscala.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特性二: scalajava不同之处在于,任何地方都可以使用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,那么可能会出现未被正确初始化的问题:

当父类的构造函数执行时,如果使用到了被子类实现或重写过的fieldfield重写或实现相当于对应的getter方法被重写),会调用子类重写或实现过的field,由于子类构造器还没有执行,所以会返回子类中被重写或实现过的该field的初始值(比如Int0Stringnull)。详细可以参数《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继承来一个长度为3classScores数组,结果PEStudent对象,只有一个长度为0classScores数组

 

此时只能使用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 traitclass,都是默认继承自一些Scala根类的,有一些基础的方法

 

Scala中,最顶端的两个traitNothingNullNull trait唯一的对象就是null

其次是继承了Nothing traitAny

接着Anyval traitAnyRef类,都继承自Any

 

Any类是个比较重要的类,其中定义了isInstanceOfasInstanceOf等方法,以及equalshashCode等对象的基本方法

Any类,有点像Java中的Object基类

AnyRef类,增加了一些多线程的方法,比如waitnotify/notifyAllsynchronized等,也是属于Java Object类的一部分

对象相等性

这里,我们要知道,在scala中,你如何判断两个引用变量,是否指向同一个对象实例

 

AnyRefeq方法用于检查两个变量是否指向同一个对象实例

AnyRefequals方法默认调用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 classcompanion 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都有一对gettersetter方法:

 

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中,可以给类、方法、fieldlocal variableconstructor / 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多线程并发安全控制

 

jvmjava虚拟机中,可以有多个线程

每个线程都有自己的工作区,还有一块儿所有线程共享的工作区

每次一个线程拿到一个公共的变量,都需要从共享区中拷贝一个副本到自己的工作区中使用和修改

然后修改完以后,再在一个合适的时机,将副本的值,写回到共享区中

 

这里就会出现一个多线程并发访问安全的问题

多个线程如果同时拷贝了变量副本,都做了不同的修改

然后依次将副本修改的值,写回到共享区中,会依次覆盖掉之前的一些副本值

就会出现变量的值,是不符合预期的

 

volatile关键字修饰的变量

它可以保证,一个线程在从共享区获取一个变量的副本时,都会强制刷新一下这个变量的值

保证自己获取到的变量的副本值是最新的

所以这样子做呢,是一种轻量级的多线程并发访问控制办法

 

但是也不是百分之百保险的,还是有可能会出现错误的风险

 

@transient var name = "leo"                             瞬态字段,不会序列化这个字段

 

之前讲序列化,默认会将一个对象中所有的字段的值,都序列化到磁盘文件中去

然后反序列化的时候,还可以获取这些字段的值

加了transient的字段,是瞬态的,序列化的时候,不会序列化这个字段

反序列化的时候,这个字段也就没有值了

 

@SerialVersionUID(value)                                  标记类的序列化版本号

 

序列化版本号,这个什么意思

如果我们将一个类的对象序列化到磁盘文件上了

结果过了一段时间以后,这个类在代码中改变了,此时如果你想将磁盘文件中的对象反序列化回来

就会报错,因为你的序列化的对象的结构与代码中的类结构已经不一样了

 

针对这种问题,就应该有一个序列化版本号

如果你的类改变了,就重新生成一个序列化版本号

反序列化的时候,就会发现序列化类型的版本号和代码中的类的版本号,不一样

 

@native                                                                    标注用c实现的本地方法

@throws(classOf[Exception]) def test() {}       给方法标记要抛出的checked异常

@varargs def test(args: String*) {}                   标记方法接收的是变长参数

@BeanProperty                                                      标记生成JavaBean风格的gettersetter方法

@BooleanBeanProperty                                      标记生成is风格的getter方法,用于boolean类型的field

@deprecated(message = "")                              让编译器提示警告

@unchecked                                                            让编译器不提示警告

xml

scalaxml有很好的支持,可以直接在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节点类型的父类型,两个重要的子类型是TextElem

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._

 

使用scalaXML类加载

val books = XML.loadFile("C://Users//Administrator//Desktop//books.xml")

 

使用JavaFileInputStream类加载

val books = XML.load(new FileInputStream("C://Users//Administrator//Desktop//books.xml"))

 

使用JavaInputStreamReader类指定加载编码

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                      在集合尾部添加元素                       SetMap

col + (ele1, ele2)       将其他集合添加到集合的尾部     SetMap

col - ele                       将元素从集合中删除                       SetMapArrayBuffer

col - (ele1, ele2)        将子集合从集合中删除                   SetMapArrayBuffer

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             取两个setdiff                                Set

col += ele                    给集合添加一个元素                       可变集合

col += (ele1, ele2)    给集合添加一个集合                       可变集合

col ++= col2               给集合添加一个集合                       可变集合

col -= ele                     从集合中删除一个元素                   可变集合

col -= (ele1, ele2)     从集合中删除一个子集合              可变集合

col = col2              从集合中删除一个子集合              可变集合

ele +=: col                  向集合头部添加一个元素              ArrayBuffer

col2 ++=: col              向集合头部添加一个集合              ArrayBuffer

集合的常用方法

headlasttail

lengthisEmpty

summaxmin

countexistsfilterfilterNot

takeWhiledropWhile

takedropsplitAt

takeRightdropRight

sclie

containsstartsWithendsWith

indexOf

intersectdiff

 

 

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

附件列表

     

    posted @ 2017-07-11 18:57  江正军  阅读(...)  评论(... 编辑 收藏