一 跳出循环语句的三种方式
1.1boolean 变量控制
var flag = true var res = 0 var n = 0
while (flag) { res += n n += 1 if (n == 5){ flag = false } }
|
1.2嵌套函数return
def add_outer() = { var res = 0 def add_inner() { for (! <- 0 until 10) { if (i == 5) { return } result += i } } add_inner() res } |
1.3使用Break对象的break方法
import scala.util.control.Breaks._ var res = 0; breakable { for(i <- 0 until 10 ){ if (i == 5) break; res += 1 } } |
二 多维数组,java数组与Scala数组的隐式转换
2.1创建多维数组
valarr = Array.ofDim[Double](3,4)
ofDim[泛型](外围数组个数,内部数组个数)
valarray = new Array[Array[Int]](3)
创建一个外部数组长度为3,内部数组长度不固定的二维数组
scala>array(0) = new Array[Int](4)
scala>array(1) = new Array[Int](4)
scala>array(2) = new Array[Int](4)
scala>array(3) = new Array[Int](4)
scala>array(0)(0) = 1
scala>array(0)(1) = 2
scala>array(0)(2) = 3
scala>array(0)(3) = 4
2.2Java数组与Scala数组的隐式转换
Scala中直接调用Java的API,可能需要传入Java类型的list
三 Tuple的拉链操作以及Javamap与Scala map隐式转换
3.1tuple拉练操作
其实就是zip操作,是Array的方法,zip操作就是将2个Array合并成一个Array
scala>var arr1 = Array(1,2,3,4,5)
arr1:Array[Int] = Array(1, 2, 3, 4, 5)
scala>var arr2 =Array("nicky","mashow","louis","lanco","trumple")
arr2:Array[String] = Array(nicky, mashow, louis, lanco, trumple)
scala>vararr3 = arr1.zip(arr2)
res20:Array[(Int, String)] = Array((1,nicky), (2,mashow), (3,louis), (4,lanco),(5,trumple))
我们可以将完成的拉练操作进行映射成Map
scala>arr3.toMap
res21:scala.collection.immutable.Map[Int,String] = Map(5 -> trumple, 1 ->nicky, 2 -> mashow, 3 -> louis, 4 -> lanco)
3.2Java Map与 Scala Map的隐式转换
importscala.collection.JavaConversions.mapAsScalaMap
valjavaScores = new java.util.HashMap[String,Int]()
javaScores.put("nicky",28)
javaScores.put("mondy",20)
javaScores.put("beyonce",25)
valscalaScores:scala.collection.mutable.Map[String,Int] = javaScores
ScalaMap 隐式转换成 JavaMap
importscala.collection.JavaConversions.mapAsJavaMap
scala>val scalaMap = Map(FAMILY->"Serif",SIZE->12)
scalaMap:scala.collection.immutable.Map[java.awt.font.TextAttribute,Any] = Map
(java.awt.font.TextAttribute(family)-> Serif, java.awt.font.TextAttribute
(size)-> 12)
scala>val font = new java.awt.Font(scalaMap) font:java.awt.
Font= java.awt.Font[family=Serif,name=Serif,style=plain,size=12]
四 扩大内部类的作用域的2种方法 以及内部类获取外部类的引用
首先,我们需要明白,Scala内部类作用域时外部类对象,而不是外部类
4.1扩大内部类作用域:伴生对象
object Class{ //伴生对象放一个内部类 class Student(val name:String) }
class Class { val students = new ArrayBuffer(Class.Student) def register(name:String) = { new Class.Student(name) } } |
4.2扩大内部类作用域:类型投影
class Class { class Student(val name:String) //表示不管Student是哪一个外部类的实例,只要是Class的内部类Student实例就行 val students = new ArrayBuffer(Class#Student) def register(name:String) = { new Class.Student(name) } } |
4.3内部类获取外部类的引用
方式一:外部类名.this.成员
class Classes(val name:String = "全真教") { var age:Int = 0; def this(age:Int){ this(); this.age = age; }
class Student(val name: String) { def info():Unit = { Classes.this.print(); println("Outer Class Name: "+Classes.this.name+"; Inner Class Name: "+name) } }
def print(){ println(this.name+" "+this.age) } } |
方式二 :别名,outrer=>
但是必须放在第一行
class Classes(val name:String = "全真教") { outer => class Student(val name: String) { def info():Unit = { outer.print(); println("Outer Class Name: "+outer.name+"; Inner Class Name: "+name) } } var age:Int = 0; def this(age:Int){ this(); this.age = age; }
def print(){ println(this.name+" "+this.age) } } |
五 import 实战详解
5.1import com.scala._这种格式可以倒入com.scala下所有成员
5.2scala 与 java不同之处在于,任何地方都可以使用import,比如类内,方法内,这种方式的好处在于,可以在一定作用域范围内使用倒入
5.3选择器、重命名、隐藏
Importjava.awt.{Color,Font} 选择器
Importjava.util.{HashMap => JavaHashMap}重命名
Importjava.util.{HashMap => _,_}导入java.util包下所有类,但是隐藏掉HashMap
5.4隐式导入
每一个scala程序默认都会倒入以下几个包成员
Importjava.lang._
Importscala._
ImportPredef._
六 重写字段提前定义,Scala继承层级,对象相等性
6.1重写字段提前定义
默认情况下,如果父类中的构造函数代码,用到了会被子类重写的字段;出现一些令人想不到的一幕:
>>子类构造函数(无参)调用父类构造函数(无参)
>>父类构造函数初始化字段(结果正确)
>>父类构造函数使用field执行其他构造代码,但是此时其他构造代码块使用了该字段,而且该字段被子类重写,那么该字段的getter方法被重写,返回0
>>子类的构造函数再执行,重写字段
>>但是此时子类从父类继承的代码,已经出现错误了
classStudent{
val classNum:Int = 10
val classScores:Array[int] = newArray[Int](classNum)
}
classPEStudent{
override val classNum:Int = 3
}
修改:
classPEStudent{
override val classNum:Int = 3
}withStudent
6.2Scala继承层级
Scala中最顶端的两个trait是Nothing和 Null,Nulltrait唯一的对象就是Null
其次是继承了Nothingtrait的Any类
接着Anyval和 AnyRef都继承自Any类
Any是一个很重要的类,其中定义了很多重要的方法,诸如isInstanceOf或者asInstanceOf以及equlashashcode等
AnyRef增加了一些多线程的方法,比如waitnotify/notify all synchronized
6.3对象相等性
在Scala如何判断2个引用变量,是否指向同一个对象实例
AnyRef的eq方法用于检查2个变量名是否指向同一个实例
AnyRef的equals方法默认就是调用eq方法实现,也就是说,默认情况下,判断2个变量相等要求必须指向同一个对象实例,此外,定义equals方法时,也最好使用相同的字段,重写hashCode方法
如果只是想要简单的通过是否指向同一个对象实例,判断变量是否相当,那么直接使用==操作符即可,默认判断null,然后调用equals方法
七 文件操作实战
7.1读取文件每一行
首先 importscala.io.Source
最后记得关闭IO: Source.close
方法一 用Source.getLines返回的迭代器
#打开文件IO流
valsource = Source.fromFile("D:/tmp/scala/info.txt")
或者指定字符集
valsource = Source.fromFile("D:/tmp/scala/info.txt","UTF-8")
#返回的迭代器
valit = source.getLines
#遍历迭代器
scala>for(line <- it) println(line)
方法二 将Source.getLines返回的迭代器转换成数组
#将迭代器转换成数组
vallines = source.getLines.toArray
方法三 调用Source.mkString返回文本所有内容
7.2遍历文件中每一个字符
直接遍历Source获取文件每一个字符
valsource = Source.fromFile("D:/tmp/scala/info.txt")
for(c<- source) println(c)
7.3以URL或者字符串形式读取字符
valhtml = Source.fromURL("http://www.baidu.com","UTF-8")
vallines = html.getLines
for(line <- lines)println(line)
valsource = Source.fromString("Hello World")
for(line<- source.getLines) println(line)
7.4结合Java IO流,读写文件
>>读取文件
importjava.io._
valfile = new File("D:/tmp/scala/info.txt")
varbytes = new Array[Byte](file.length.toInt)
valfis = new FileInputStream(file)
fis.read(bytes)
fis.close
>>写入文件
valpw = new PrintWriter("D:/tmp/scala/read.txt")
pw.println("2.1deploymentTopology.xml")
pw.flush
pw.close
>>文件拷贝
valfis = new FileInputStream("D:/tmp/scala/info.txt")
valfos = new FileOutputStream("D:/tmp/scala/base.txt")
valbuf = new Array[Byte](1024)
fis.read(buf)
fos.write(buf,0,1024)
fos.flush
fis.close
fos.close
7.5递归遍历子目录
defiterateDir(dir:File):Iterator[File] = {
val subDirs =dir.listFiles.filter(_.isDirectory)
subDirs.toIterator ++ subDirs.toIterator.flatMap(iterateDir_)
}
subDirs.toIterator.flatMap(iterateDir_)//递归调用
flatMap:map +flatten 把嵌套的map的扁平化
++表示两个集合合成一个新的集合,在list中等价于:::
vallist =List("nicky","mondy","judy","cloudera")
scala>list ++ List("Mexi","Chrois","Jodan")
res2:List[String] = List(nicky, mondy, judy, cloudera, Mexi, Chrois, Jodan)
scala>list ::: List("Mexi","Chrois","Jodan")
res3:List[String] = List(nicky, mondy, judy, cloudera, Mexi, Chrois, Jodan)
7.6序列化以及反序列化
@SerialVersionUID(42L) class Person(val name:String) extends Seriali zable val p1 = new Person("nicky") import java.io._ val oos = new ObjectOutputStream(new FileOutputStream ("D:/tmp/scala /test.obj")) oos.writeObject(p1) oos.close val ois = new ObjectInputStream(new FileInputStream("D:/tmp/scala /test.obj")) val restoreNicky = ois.readObject().asInstanceOf[Person] restoreNicky.name |
八 偏函数实战
偏函数:一种函数的高级形式,就是没有明确定义输出参数的函数,函数体就是一连串的case语句
偏函数是PartialFucntion[A,B]类的一个实例,这个类有2个方法
一个是apply方法,直接调用可以通过函数体内的case进行匹配,返回结果;另外一函数是isDefinedAt方法,可以返回一个输入,是否跟任何一个case语句匹配
defpartial:PartialFunction[String,String] = {
case"x"=>"nicky";case"y"=>"mondy";case "z"=>"kettle"
}
scala>partial("x")
res8:String = nicky
scala>partial("y")
res9:String = mondy
scala>partial.isDefinedAt("x")
res10:Boolean = true
九 执行外部命令
我们scala程序是运行在JVM进程中,如果向执行进程之外的一些东西,比如本地操作系统命令。如果想要实现这个功能:
需要先导入一个包: importsys.process._
我们将命令封装在字符串里,然后后面接一个!
语法:"命令"!
例子:
"netstat-aon"!
"java-version"!
十 正则表达式
Scala定义一个正则表达式,使用String类的r方法
语法:val regex= "正则表达式".r
此时返回一个类型scala.util.matching.Regex
其实这就相当于:
valregex = new scala.util.matching.Regex("正则表达式")
>>获取一个字符串中 正则表达式匹配的部分
valregex = "[0-9]+".r
scala>for(line <- regex.findAllIn("09 az $#")) println(line)
09
>>获取一个字符串中第一个被匹配的部分
scala>for(line <- regex.findFirstIn("09 az $#")) println(line)
09
>>replaceAllIn将匹配的部分替换掉
regex.replaceAllIn("正则部分","替换部分")
>>replaceFirstIn将第一个匹配的替换掉
regex.replaceFirstIn ("正则部分","替换部分")
十一 提取器实战
提取器:就是一个包含unapply方法的对象,跟apply方法正好相反。
apply方法:接收一堆参数,然后构造出一个对象
unapply方法:是接受一个字符串,然后解析出对象的属性值
class Person(val name:String, val age:Int) object Person{ def unapply(str:String) = { val splitIndex = str.indexOf(" ") if(splitIndex == -1) None else Some((str.substring(0,splitIndex),str.substring(splitIndex+1))) } def apply(name:String,age:Int):Person = { new Person(name,age) } } val Person(name,age) = "nicky 28" |
name:String = nicky
age:String = 25
样例类的提取器:
样例类类似于java的javabean,有字段和get/set方法
caseclass Person(name:String,age:Int)
特点:
默认生成get/set方法
自动生成object伴生对象,并且提供apply方法
所以我们可以直接Person(name,age)
valp = Person("nicky",10)
pmatch {
casePerson(name,age) => println(name+" "+age)//样例类提取器
}
nicky10
只有一个参数的提取器
classHuman(name:String)
objectHuman {
def unapply(input:String):Option[String] =Some(input)
}
scala>val Human(name) ="nicky"
name:String = nicky
十二 注解详解
12.1注解基础和介绍
在哪些地方可以加注解
Scala可以给类、方法、字段、变量、参数甚至类型参数添加注解,而且支持给某个对象添加多个注解
但是,这里有一些特例:
>>如果要给主构造函数添加注解,那么需要在构造函数前添加注解,并且加上一对圆括号
classPerson @unchecked() (val name:String, val age:Int)
>>给表达式添加注解,此时需要在表达式后面加冒号以及注解,比如
valscores = Map("Leo"->90,"Jack"->60)
scores.get("Leo"):@uncheckedmatch {
case score => println(score)
}
Scala中要自己开发注解,就必须扩展Annotationtrait
classTest extends annotation.Annotation
@Testclass MyTest
注解参数:
classTest(var timeout:Int) extends annotation.Annotation
@Test(timeout=10)class MyTest
如果注解参数名时value,我们在使用时,可以不加参数名
classLink(var value:String) extends annotation.Annotation
@Link("www.baidu.com")class MyTest
12.2Scala的常用的内置注解
>>轻量级的java多线程并发安全控制
@volatilevar name ="content"
>>瞬态字段,不会被序列化
@transientvar name ="content"
>>@native标注用c实现的本地方法
@native
>>标记类的序列化版本号
@SerialVersionUID(value)
>>给方法标记要抛出的异常
@throw(classOf[Exception])def test(){}
>>标记方法接收变长参数
@varargsdef test(args:String*){}
>>标记生成JavaBean风格的get/set方法
@BeanProperty
>>标记生成is风格的get/set方法,用于布尔类型字段
@BooleanBeanProperty
>>标记过时
@deprecated
>>不检查编译警告
@unchecked
十三XML基础介绍
13.1XML 节点类型
Node类所有XML节点类型的父类型,2个重要的子类是Text和Elem
Elem表示一个XML元素,也就是一个XML节点。Scala.xml.Elem类型的label属性,返回的是标签名,child属性返回的是子元素
scala.xml.NodeSeq类型,是一个元素序列,可以用for循环,直接遍历它
可以通过scala.xml.NodeBuffer类型,手动创建一个节点序列
valbookbuf = new scala.xml.NodeBuffer
bookbuf+= <book>book1</book>
bookbuf+= <book>book2</book>
valbooks:NodeSeq = bookbuf
13.2XML 元素属性
scala.xml.Elem.attributes属性,可以返回xml元素的属性,是Seq[
scala.xml.Node]类型的,继续调用text属性,可以拿到属性值
valbooks = <books><book id="book1"><name>西游记</name><price>
100</price></book><bookid="book2"><name>红楼梦</name><price>
88</price></book></books>
遍历子节点:
for(node<- books.child) println(node.attributes("id"))
获取属性Map:book.attributes.asAttrMap
13.3在XML中嵌入scala代码
scala>val books = Array("Think In Java","Hadoop In Action")
books:Array[String] = Array(Think In Java, Hadoop In Action)
s<books><book>{books(0)}</book><book>{books(1)}</book></books>
res0:scala.xml.Elem = <books><book>Think InJava</book><book>
HadoopIn Action</book></books>
<books>{for(book<- books ) yield <book>{book}</book>}</books>
还可以在xml 标签属性中嵌入scala代码
<books><bookid="${books(0)}">${books(0)}</book></books>
13.4修改元素
默认情况下,Scala中xml元素默认是不可变的;如果你要修改,必须拷贝一份再修改
valbooks = <books><book id="book1">三国演义</book><bookid="book2">西厢记</book></books>
添加一个节点
books.copy(child= books.child ++ <book id="book3">水浒传</book>)
修改属性
valbook = <book id="book1">三国演义</book>
book% Attribute(null,"id","book2",Null)
添加属性
book%Attribute(null,"id","book2",Attribute(null,"price","20.45",Null))
13.5XML加载和写入外部文档
importscala.xml._
使用ScalaXML类加载
valbooks = XML.loadFile("D:/books.xml")
importjava.io._
使用javaFileInputStream 加载
valbooks = XML.load(new FileInputStream("D:/books.xml"))
将内存中XML写入外部文档
XML.save("D:/books.xml",books)
十四 集合
14.1集合元素操作
vallist =List("nicky","mondy","shelly","carl")
添加元素到集合头部,仅适合Seq
"darling"+: list
添加元素到集合尾部,仅适合Seq
list:+ "louis"
Set,Map尾部添加元素
valset = Set("x","y")
set + "z"
将元素从集合删除(Set,Map,ArrayBuffer)
valb = new ArrayBuffer[String]()
b+= "qa"
b–"qa"
set- "x"
将子集和从集合中删除
vals = Set(Set("1","2"),Set("x","y"))
s- Set("1","2")
s-(Set("1","2"),Set("x","y"))
其他集合添加到集合尾部
valcol1 = List("1","2")
valcol2 = List("x","y")
col1++col2
其他集合添加到集合头部
col2++: col1
将元素添加到list头部
"belly":: col1
其他list追加到list头部
vall = List("xa")
l::: list
其他list追加到list尾部
list::: l
set集合并集
set1| set2
set集合交集
set1& set2
取被比较的set集合交集的剩余部分
scala>set1 &~ set2
res36:scala.collection.mutable.Set[String] = Set(y, z)
scala>set2 &~ set1
res37:scala.collection.mutable.Set[String] = Set(v, w)
ArrayBuffer头部添加元素
"m"+=: b
ArrayBuffer集合头部加集合
c++=: b
14.2集合常用操作方法
vararr = scala.collection.mutable.ArrayBuffer(1,2,3,4)
>>headlast tail
arr.head= 1
arr.last= 4
arr.tail= (2,3,4)
>>lengthisEmpty
arr.length=4
arr.isEmpty= true
>>summax min
arr.sum= 10
arr.max= 4
arr.min= 1
>>count(表达式)exists(表达式)filter(表达式)filterNot(表达式)
arr.count(_% 2 == 0) =2
arr.exists(_% 2 == 0) = true
arr.filter(_% 2 == 0) = (2,4)
arr.filterNot(_% 2 == 0) =(1,3)
>>filtertakeWhile(表达式)dropWhile(表达式)
takeWhile:取得满足表达式元素,直到碰到第一个不满足的为止
scala>arr.takeWhile(_ % 1 == 0)
res77:scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4)
返回四个,因为每一个都满足
scala>arr.takeWhile(_ % 2 == 0)
res78:scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()
一个也没返回,因为第一个元素%2 != 0,由于不满足,所以后面满足条件的也不会取
dropWhile:删除满足条件的元素,直到第一个不满足的条件为止,就不删除了
由于第一个不满足条件 所以没删除
scala>arr.dropWhile(_ % 2 == 0)
res79:scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4)
第一个满足条件,第二个不满足条件,所以只删除了第一个元素
scala>arr.dropWhile(_ % 2 == 1)
res80:scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(2, 3, 4)
>>take(int)drop(int) splitAt(i)
take(i)取前几个元素
scala>arr.take(3)
res85:scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3)
drop(i)删除前几个元素
scala>arr.drop(2)
res87:scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(3, 4)
splitAt(i)从第几个元素分割成多个
scala>arr.splitAt(3)
res90:(scala.collection.mutable.ArrayBuffer[Int], scala.collection.
mutable.ArrayBuffer[Int])= (ArrayBuffer(1, 2, 3),ArrayBuffer(4))
>>takeRight(int)dropRight(int)
takeRight(int): 从右边开始去取,反向取
dropRight(int):从右边开始删除
>>slice(fromIndex,toIndex)截取
scala>arr.slice(1,3)
res96:scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(2, 3)
>>containsstartsWith endsWith
contains(元素):是否包含某个元素
scala>arr.contains(4)
res98:Boolean = true
startsWith(ArrayBuffer):是否以某个序列开始
scala>arr.startsWith(ArrayBuffer(1,2))
res103:Boolean = true
endsWith(ArrayBuffer):是否以某个序列结束
scala>arr.endsWith(ArrayBuffer(3,4))
res104:Boolean = true
>>indexOf(元素):计算元素的索引位置
scala>arr.indexOf(4)
res105:Int = 3
>>intersectdiff
intersect(集合):取交集
scala>arr.intersect(arr1)
res106:scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(4)
diff(集合): 取被比较的集合差集
scala>arr.diff(arr1)
res108:scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3)
十五 map flatmap collect foreach 详解
15.1map 一对一映射
vallist = List("x","y","z")
list.map(_+ "M")
遍历每一个list元素,然后一一映射,映射规则每个元素+后缀M
生成一个新的List
15.2flatMap操作,一对多映射,将集合或者层级结构的数据,扁平化
valscoreMap =Map("nicky"->List(90.87,89),"leo"->List(97,45,67))
valnames = List("nicky","leo")
names.flatMap(scoreMap(_))
表示先进行map映射,在进行flatten操作
可以分解为2个步骤
scala>val results = names.map(scoreMap(_))
results:List[List[AnyVal]] = List(List(90.87, 89.0), List(97, 45, 67))
scala>results.flatten
res119:List[AnyVal] = List(90.87, 89.0, 97, 45, 67)
15.3collect 一般结合偏函数使用
scala>def func:PartialFunction[Int,String] = {case 1 => "abc";case 2=> "xyz"}
func:PartialFunction[Int,String]
scala>List(1,2).collect(func)
res120:List[String] = List(abc, xyz)
15.4foreach 简化遍历
List(1,2,3,4).foreach(println_)
十六 reduce 和 fold实战
16.1reduceLeft & reduceRight
reduceLeft:左边元素和右边元素运算,然后再右边的元素和前面的中间结果运算,一直到最后
scala>List(1,2,3,4,5).reduceLeft(_ - _)
res14:Int = -13
过程分析:
1-2= -1
-1-3= -4
-4-4= -8
-8-5= -13
reduceRight:右边元素和左边元素运算,然后再右边的元素和前面的中间结果运算,一直到最后
scala>List(1,2,3,4,5).reduceRight(_ - _)
res15:Int = 3
过程分析:
4-5=-1
3-(-1)=4
2-4=-2
1-(-2)=3
16.2foldLeft & foldRight
foldLeft:指定一个初始值,然后从左往右进行运算,指定的值和第一个元素运算,然后结果和第二个元素进行运算,一直到最后
scala> List(1,2,3,4,5).foldLeft(15)(_ - _)
res17:Int = 0
过程分析:
15-1=14
14-2=12
12-3=9
9-4=5
5-5=0
foldRight:元素从右到左,第一个元素和指定元素计算,第二各元素和前面的结果进行运算,一直到最后
scala>List(1,2,3,4,5).foldRight(15)(_ - _)
res18:Int = -12
过程分析:
5-15=-10
4-(-10)=14
3-14=-11
2-(-11)=13
1-13=-12