scala程序开发入门

scala程序开发入门,快速步入scala的门槛:java

一、Scala的特性:

A、纯粹面向对象(没有基本类型,只有对象类型)、Scala的安装与JDK相同,只须要解压以后配置环境变量便可;
B、Scala在安装以前必须先安装JDK,由于Scala的编译结果是中间字节码文件,它须要在JVM上运行,Scala能够调用Java类库来完成某些功能;
C、Scala相似于python,一半面向过程一半面向对象,还能够基于shell的命令行进行操做,固然也能够像Java那样先使用scalac编译成中间字节码以后再使用scala解释执行它,注意保存的Scala源文件名称必须以scala做为扩展名,且文件名必须与对象名彻底匹配;
D、Scala相似于JS脚本,区分大小写,以换行符为单位来定义语句,可是若是须要在同一行定义多条语句则必须使用分号隔开;
E、执行速度对比:C(C++)>Java(Scala)>Python(Ruby);
F、Scala数据类型:
Byte(1byte)、Short(2byte)、Int(4byte)、Long(8byte)、Float(4byte)、Double(8byte)、Char(2byte)、String(取决于字符数量)、Boolean(1byte)、Unit(至关于void)、Null(至关于null)、Nothing(全部类的子类型)、Any(全部基本类型和引用类型的根类型)、AnyVal(全部基本类型的超类型)、AnyRef(全部引用类型的超类型)。python

//null值只能被推断为Null类型,null表明空值,它能够被赋值给任何AnyRef类型的常量或变量
scala> var kk=null 
kk: Null = null

//定义Boolean类型
scala> val flag=true
flag: Boolean = true

//定义Char类型 
scala> val flag='A'
flag: Char = A

//小数默认推断为Double类型
scala> val height=1.67 
height: Double = 1.67

//整数默认推断为Int类型
scala> val age=30 
age: Int = 30

//定义字符串类型(字串常量必须使用双引号),显示的结果类型为String,它是java.lang.String的缩写形式
scala> val name="mmzs" 
name: String = mmzs

//Int类型能够自动向上转型为超类型AnyVal
scala> val age2:AnyVal=age
age2: AnyVal = 30
//String类型为引用类型,不能上转型到AnyVal,由于它们之间没有继承关系
scala> val name2:AnyVal=name 
<console>:12: error: the result type of an implicit conversion must be more specific than AnyVal
val name2:AnyVal=name
^
//可是能够自动上转型为超类型AnyRef,由于AnyRef是全部引用类型的超类型
scala> val name2:AnyRef=name 
name2: AnyRef = mmzs
//一样的,Int为基本类型,也不能上转型到AnyRef,由于它们之间没有继承关系
scala> val age2:AnyRef=age 
<console>:12: error: the result type of an implicit conversion must be more specific than AnyRef
val age2:AnyRef=age
^
//可是全部的基本类型(AnyVal)和引用类型(AnyRef)均可以自动上转型为根类型Any    
scala> val kkm:Any=age
kkm: Any = 30
scala> val kkm:Any=name
kkm: Any = mmzs
scala> val kkm:Any=age2
kkm: Any = 100
scala> val kkm:Any=name2
kkm: Any = mmzs
scala>:quit //也能够直接:q

注意:
对任何Scala对象的显式类型定义其前面都有一个以英文冒号做为前缀的类型标注,这是Scala的语法所决定的,如:val abc:Int=12三、def getName():String={............}
8中基本类型都是scala核心包中的,scala包至关于Java中的java.lang包
Scala是强类型的语言,并非弱类型语言,虽然你并无显式指定变量的类型,可是Scala会根据你赋的值自动进行类型推断,一旦推断出变量的类型,后续使用该变量就必须符合类型兼容原则
定义变量或函数的通式是:val[var|def] varName|funName(...):type=value|{.....} 程序员

G、字面常量:
单引号:表示单个字符
双引号:表示字符串
三双引号:表示具有可换行的多行字符串es6

//三双引号实例
scala> val info="""my name is mmzs
| my age is 30"""
info: String =
my name is mmzs
my age is 30    

H、字符串之间以及字符串与变量、表达式之间的拼接直接使用重载的加号("+")
I、Scala中没有static的概念,全部处于object定义对象中的成员都是静态的,入口方法main必须被定义在object定义的类中,同时源文件的名字必须与object定义的对象名同名,一个源文件中可使用class定义多个类,
也可使用object定义多个对象,可是做为运行的入口main方法永远都是处于与源文件名相同的对象体内
J、Scala中的全部常量和变量(包括成员常量、变量和局部常量、变量)都必须先定义和初始化而后才能被使用,暂时不能肯定值的常量、变量能够被初始化为空值(即0,初始化为0意味着数字类型被赋值为0,字符类型被赋值为空格、布尔类型被赋值为false,引用类型所有被赋值为null),如:sql

scala> var age:Int=0
age: Int = 0
scala> var height:Double=0
height: Double = 0.0
scala> var code:Char=0
code: Char = ?
scala> var flag:Boolean=false
flag: Boolean = false
scala> var name:String=null
name: String = null

二、基础语法:

A、变量的声明:

//声明变量(变量的值能够被从新赋值以改变它的值)
var VariableName:DataType=Initial Value
//声明常量(常量的值不能够被从新赋值,值不能被改变)
val VariableName:DataType=Initial Valueshell

//val定义的是常量,不能被从新赋值,可是可使用val从新定义它,如:
scala> val ab=20
ab: Int = 20 //自动推断为Int类型
scala> ab=50 //此处常量ab不能被从新赋值
<console>:12: error: reassignment to val
       ab=50
         ^
scala> val ab=50 //可是能够从新定义该常量
ab: Int = 50

//var定义的是变量,能够被从新赋值,如:
scala> var ab=220
ab: Int = 220
scala> ab=50 //此处ab变量被从新赋值
ab: Int = 50
scala> var ab=50.05 //也能够被从新定义
ab: Double = 50.05

//val与var定义的常量或变量均可以相互被对方重定义,重定义时能够被定义成其它类型
scala> val ab=100
ab: Int = 100
scala> var ab=true
ab: Boolean = true
scala> val ab='A'
ab: Char = A     
scala> var ab=45.55
ab: Double = 45.55
scala> ab=true //变量的值能够被从新赋值,可是所赋的值必须与定义该变量时的类型匹配
<console>:12: error: type mismatch;
 found   : Boolean(true)
 required: Double
       ab=true
          ^
scala> ab='A' //隐式将Char类型按ASCII码转换成Double类型,这是符合自动类型转换规则的ab: Double = 65.0    

B、变量的类型与赋值表达式

  在 Scala 中声明变量和常量不必定要指明数据类型,在没有指明数据类型的状况下,其数据类型是经过变量或常量的初始值来进行推断,能够看出Scala语言其实是一种强类型的编程语言,Scala声明变量的语法有点相似于JS但与JS也有区别,区别在于JS中的var关键字不是必须的,且JS中的每个变量能够自由灵活的被赋值为任何类型的值;可是Scala语言中的var或val关键字是必须的,且一旦使用这些关键字定义了变量或常量的类型(根据定义时的初始值进行类型自动推断)以后,后续对该变量的赋值就必须符合类型兼容原则,若是该变量须要被赋值为其它类型则只能使用val或var对其从新定义编程

var myVar = 10;//声明变量
val myVal = "Hello,Scala!";//声明常量
//以上实例中,myVar 会被推断为Int类型,myVal会被推断为String类型
//使用枚举法同时声明多个相同类型和值的变量
scala> val a,b=100 //a,b都声明初始值为100,都被推断为Int类型
a: Int = 100
b: Int = 100
scala> a
res1: Int = 100
scala> b
res2: Int = 100
//使用元组法同时声明多个不一样类型和值的变量 
scala> val (a,b)=(100,"mmzs")
a: Int = 100
b: String = mmzs
//使用元组法时也能够显式指定类型
scala> val (a:Int,b:String)=(100,"mmzs")
a: Int = 100
b: String = mmzs
//注意下面这种方式其实是枚举法的赋值方式,它表示将右边的元组对象同时赋值给左边的a和b两个变量
scala> val a,b=(100,"mmzs")
a: (Int, String) = (100,mmzs)
b: (Int, String) = (100,mmzs)

C、Scala命令行换行
C一、可使用三双引号输入多行数据
C二、若是输入了错误的内容致使命令等待能够连续两次回车以结束输入数组

D、函数定义

def max(a:Int,b:Int):Int={
  ......
}

说明:
函数定义以def开始,函数参数名后面必须指定类型,由于Scala没法自动推断出函数的参数类型;
函数若是被设计为递归函数(在函数体中调用它自身)则函数的返回类型就不能被省略,即递归函数没法推断函数的返回类型;
函数体实际上也被称之为函数块,一个块中若是只有一条语句则能够省略块两端的大括号,如:def max2(x: Int, y: Int) = if (x > y) x else yapp

//函数的调用与表达式的调用相似,以下:
scala> def max2(x: Int, y: Int) = if (x > y) x else y
max2: (x: Int, y: Int)Int
scala> max2(3,5)
res4: Int = 5
//函数也能够被推断为Unit类型:
scala> def greet()=println("Hello, world!")
greet: ()Unit
//下面的函数定义返回Int类型,可是实际上函数被推断为不返回任何值,即Unit类型,
scala> def greet():Int=println("Hello, world!")
<console>:11: error: type mismatch; //实际返回的类型与定义的返回类型不匹配
found : Unit //实际的返回类型
required: Int //定义的返回类型
def greet():Int=println("Hello, world!")

E、Scala脚本编写与调用

#编写Scala脚本:编程语言

[root@CloudDeskTop install]# vi test.scala
[root@CloudDeskTop install]# cat test.scala
//使用args数组接收传入脚本的参数
println("Hello:"+args(0))
println("Hello:"+args(1))
[root@CloudDeskTop install]# scala test.scala liming zhangsan
Hello:liming
Hello:zhangsan

说明:
scala脚本(test.scala)中可使用args数组来得到传入脚本(test.scala)的参数,注意取参数所用的是小括号,不是中括号;scala脚本中的注释与Java中的注释是相同的,使用双斜杠标记单行注释,使用/*......*/标记多行注释。

F、while循环

F一、Scala中没有++i、--i、i++、i--的运算模式,可使用i=i+一、i+=1来予以代替
F二、Scala中默认一行一条语句,若是须要在同一行放置多条语句则应该使用英文分号隔开
F三、Scala与Java相同,if、switch、while、for等后面的条件表达式都须要使用小括号括起来,这与Python、Ruby是不一样的

F、for循环

scala> val list=List("zhu","ge","liang")
list: List[String] = List(zhu, ge, liang)

scala> for(ele<-list) println(ele)
zhu
ge
liang
//说明:因为for循环体中就只有一条语句(println(arg))因此省略了大括号

for表达式中<-后面的参数是一个集合,前面的参数是一个迭代变量,该迭代变量是隐式的val声明常量,在循环块中不能改变它的值,可是每次迭代都将从新定义和初始化它的值

scala> for(ele<-list){ele="sdnj";println(ele)} //试图在循环体中对它赋值是错误的
<console>:13: error: reassignment to val //由于它被指定为常量,尽管如此,你不能显式的写成for(val ele<-list){println(ele)},这是语法规定所需
for(ele<-list){ele="sdnj";println(ele)}

G、函数式编程(函数自己做为参数传递)

foreach迭代函数
foreach函数的参数接收一个函数对象,函数对象的=>符号左边是参数列表,右边是函数体,参数arg的类型没有指定则经过Scala来自动推断,而函数的返回类型没有指定也是经过函数体的返回值来进行自动推断,函数的参数函数是经过隐函数来定义的;

隐函数的特征:
A、隐函数的定义中没有关键字def和函数名
B、隐函数的参数类型并非必须的,它能够经过实参值进行自动类型推断,这是与显函数定义不一样的地方
C、隐函数的参数列表中若是只有一个参数则没有给定参数类型的状况下其参数两端的小括号能够省略,甚至
能够直接省略掉参数的定义和赋值符号,直接造成偏函数的定义
D、隐函数无需定义返回类型,返回类型根据函数体自动推断
D、函数体的赋值符号是=>,而不是=

[root@CloudDeskTop install]# vi test.scala
[root@CloudDeskTop install]# cat test.scala 
args.foreach(arg=>println(arg))
[root@CloudDeskTop install]# scala test.scala zhu ge liang
zhu
ge
liang

小结:
全部变量、常量、函数在定义时其类型(对于函数而言是返回类型)是可选的(若是显式的给定类型则给定类型将做为自动化类型推断结果的一种校验),对于函数的参数类型分两种状况,在显式定义一个命名函数时其参数类型不可省略,在函数式编程领域中其参数函数的参数类型则是可选的(一样,若是显式的给定类型则给定类型将做为自动化类型推断结果的一种校验)

//若是须要显式指定参数类型则须要使用小括号将参数列表括起来:
scala> val list=List("zhu","ge","liang")
list: List[String] = List(zhu, ge, liang)
scala> list.foreach((ele:String)=>println(ele))//注意foreach不能对ArrayList集合遍历
zhu
ge
liang

//若是存在多个参数则函数定义格式以下:
(key:String,value:Object)=>println(key+":"+value)

小结:
在Scala中任何类型的变量在定义时都必须初始化(包括类),类在定义时使用类体初始化类的定义(但在Scala中官方并不认为这被称为初始化,由于它没有赋值符号),

函数在定义时使用函数体进行初始化,赋值符号是=(显函数定义)或=>(隐函数定义),常量和变量在定义时使用常量值或变量值进行初始化,赋值符号是=

//若是只有一个参数则能够省略参数定义和箭头部分(这种书写方式被称为偏应用函数)
scala> list.foreach(println)
zhu
ge
liang

注意:实际上foreach函数是for循环的变体,本质上都是相同的迭代方式,for循环的可读性更高,但foreach函数是标准的函数式编程写法

H、数组

H一、通用方式建立数组和使用数组

//当没有指定泛型参数时默认数组元素类型为Nothing类型,与Any类型相反,Nothing表明全部类的子类型,这意味着任何类型的数据不通过强制下转型将没法放入数组中去

//小括号中的参数2表明数组的长度(即数组中元素的个数),Scala根据此参数初始化数组的长度,这与Java不一样,Scala中初始化数组的长度、下标的访问都是使用小括号,而不是方括号

scala> var arr=new Array(2);
arr: Array[Nothing] = Array(null, null)

scala> arr(0)="zhugeliang"
<console>:13: error: type mismatch;
found : String("zhugeliang")
required: Nothing
arr(0)="zhugeliang"
^

注意: 从上面实例能够发现使用数组长度来初始化Scala数组时必须指定其泛型,不然数组中将没法放入任何元素

//指定了泛型以后就能够直接放入元素了
scala> var arr=new Array[String](3);
//若是须要显式指定变量类型能够像下面这样定义,能够看到泛型其实是类型的一部分
scala> var arr:Array[String]=new Array[String](3);
arr: Array[String] = Array(null, null, null)

scala> arr(0)="mmzs"
scala> arr(0)
res1: String = mmzs
scala> arr(1)="淼淼之森"
scala> arr(2)="mmzsblog"

//foreach并传递隐函数遍历
scala> arr.foreach(ele=>println(ele))
mmzs
淼淼之森
mmzsblog

//foreach并传递偏函数遍历
scala> arr.foreach(println)
mmzs
淼淼之森
mmzsblog

//使用for循环遍历
scala> for(ele<-arr)println(ele)
mmzs
淼淼之森
mmzsblog

//使用for循环并构建下标集合遍历,to关键字其实是一个带一个Int参数的方法,0 to 2被解释成(0).to(2),to方法返回的是一个序列,arr.length描述集合的长度
scala> for(i<-0 to arr.length-1)println(arr(i))
mmzs
淼淼之森
mmzsblog

说明:
从技术上讲,Scala中没有操做符重载的概念,全部的操做符都将被视为方法而后实现对方法的调用,这也是由于Scala中的数组实际上就是一个普通Scala类的实现而已,
对数组类长度和下标的访问都是对该类中相应方法的调用,这也是下标的访问为何是小括号而不是方括号的缘由,好比对算术运算符+、-、*、/的访问也是视为方法来调用的:

scala> 1+2
res23: Int = 3
scala> 1.+(2)
res24: Int = 3
scala> (1).+(2)
res25: Int = 3

这个原则不只仅局限于数组:任何对某些在括号中的参数的对象的应用将都被转换为对工厂方法apply(工厂方法apply是一个带可变长度参数的方法)的调用。固然前提是这个类型实际定义过apply方法。
因此这是一个通则,上面对数组的读写过程实际上被解释为:

scala> var arr:Array[String]=new Array[String](3);
arr: Array[String] = Array(null, null, null)
scala> arr.update(0,"zhu")
scala> arr.update(1,"ge")
scala> arr.update(2,"liang")
scala> for(i<-0.to(2))println(arr.apply(i))
zhu
ge
liang

H二、简洁方式建立数组和使用数组

//注意这种模式下不能再使用new关键字,同时也无需指定数组的泛型和长度,由于在建立数组的同时已经用实际的元素类型和值初始化了

scala> val arr=Array("zhu","ge","liang")
arr: Array[String] = Array(zhu, ge, liang)

scala> arr.apply(0)
res8: String = zhu
scala> arr.apply(1)
res9: String = ge

//也能够直接使用apply方法来建立数组(这种调用是直接对底层工厂方法的调用,效率或许更高,可是代码表现得有点啰嗦)

scala> val arr=Array.apply("zhuzhu","gege","liangliang")
arr: Array[String] = Array(zhuzhu, gege, liangliang)

scala> arr.apply(1)
res10: String = gege

scala> arr(1)
res11: String = gege
//因为数组元素的类型所有都是些基本类型,因而数组元素的类型被推断为AnyVal类型
scala> val arr=Array.apply(36,1.67,'A',true)
arr: Array[AnyVal] = Array(36, 1.67, A, true)

//因为数组元素的类型所有都是些引用类型,因而数组元素的类型被推断为Object类型,在Java平台上,AnyRef是java.lang.Object类的别名,在.Net平台上,AnyRef是system.Object类的别名
scala> class User{}
defined class User

scala> val arr=Array.apply("zhugeliang",new User())
arr: Array[Object] = Array(zhugeliang, User@c9cd85d)

//因为初始化指定的类型既有引用类型又有基本类型,因此数组元素的类型被推断为Any类型
scala> val arr=Array.apply("liubei",39,1.77)
arr: Array[Any] = Array(liubei, 39, 1.77)

//使用简洁方式建立数组时最好不要加泛型,这样可使得Scala完成自动泛型推断,若是自定义了泛型则要求数组中的元素所有与泛型兼容,不然将抛出类型不匹配的异常
scala> val arr=Array[String]("ligang",23)
<console>:11: error: type mismatch;
found : Int(23)
required: String
val arr=Array[String]("ligang",23)
^

小结:
使用new关键字建立数组时,小括号中的参数是惟一的,且是必选参数,并且必须是一个表明数组长度的整型数据,使用简洁方式建立数组时小括号中的参数是0到多个可选的参数值,这些数据是数组中的元素值,因为建立数组时没有指定泛型则默认泛型为Nothing类型,因此对于泛型而言有如下建议:
  a、若是使用new方式建立数组则必须指定泛型,不然后续没法为Nothing类型的数组元素赋值,使用new方式建立数组适合于数组中元素较多没法一次性枚举出来的状况使用
  b、若是使用简洁方式建立数组则建议使用Scala的自动类型推断,但你也能够强制指定泛型,以保证初始化的枚举值类型都是准确无误的,使用简洁方式建立数组适合于数组中元素较少能够一次性枚举出来的状况使用

I、列表List

Scala中的List(全名是scala.List)不一样于Java中的java.util.List类型,Scala中的List与字符串String的操做相似,它是一种彻底不可变的列表,任何对列表的增、删、改操做都将产生一个新的列表对象返回,须要注意的是List与数组类型Array也不见得彻底相同,虽然他们的长度都是不能够改变的,可是数组中的元素值则是能够改变的,而List集合中的元素值不能被改变,一旦改变将产生新的列表对象
a、合并两个列表并返回一个新的列表

//建立两个列表,泛型根据初始化的元素类型自动推断为Any
scala> val myinfo=List("zhugeliang",39,1.77)
myinfo: List[Any] = List(zhugeliang, 39, 1.77)
scala> val youinfo=List("lingang",50,1.68)
youinfo: List[Any] = List(lingang, 50, 1.68)
//合并两个列表中的元素(按顺序合并)
scala> val info=myinfo:::youinfo
info: List[Any] = List(zhugeliang, 39, 1.77, lingang, 50, 1.68)
//合并后产生新的列表,地址再也不相同
scala> myinfo==info
res1: Boolean = false
scala> youinfo==info
res2: Boolean = false

b、在列表的首部插入数据,返回一个新的列表

scala> val list=List(68,86,99)
list: List[Int] = List(68, 86, 99)

scala> val list2=100::list
list2: List[Int] = List(100, 68, 86, 99)

scala> list==list2
res14: Boolean = false

说明:
不能使用new关键字实例化List对象,由于List.apply()方法被定义在scala.List伴生对象上;
以冒号结尾的方法名表示调用关系上的反转,即调用方法的对象是右边的操做数,像上面的myinfo:::youinfo表示的调用关系是youinfo.:::(myinfo),而100::list表示的调用关系是list.::(100),如:

scala> list2.::(200)
res15: List[Int] = List(200, 100, 68, 86, 99)

scala> info.:::(myinfo)
res16: List[Any] = List(zhugeliang, 39, 1.77, zhugeliang, 39, 1.77, lingang, 50, 1.68)

而那些没有以英文冒号结尾的关键字表示的方法名则调用方法的对象是左边的操做数

c、串联离散数据成列表对象

//使用Nil关键字能够建立一个空列表
scala> Nil
res15: scala.collection.immutable.Nil.type = List()
scala> val list3="ligang"::27::1.67::Nil
list3: List[Any] = List(ligang, 27, 1.67)
//也可使用成员运算符调用方法,可是表现的很啰嗦
scala> val list4=Nil.::("liubei").::(36).::(1.67)
list4: List[Any] = List(1.67, 36, liubei)

d、列表元素访问

//访问列表中索引为2的元素值
scala> list4(2)
res16: Any = liubei
//计算列表的尺寸,length属性几乎是全部集合所具有的属性,它用于表针集合的尺寸
scala> list4.length
res18: Int = 3
//返回列表的第一个元素或最后一个元素
scala> info.head
res19: Any = liubei
scala> info.last
res20: Any = 1.77
//列表是否为空列表
scala> Nil.isEmpty
res21: Boolean = true
scala> info.isEmpty
res22: Boolean = false
//返回除去第一个元素后的列表
scala> List(10,20,30,40).tail
res28: List[Int] = List(20, 30, 40)
//返回除去最后一个元素后的列表
scala> List(10,20,30,40).init
res27: List[Int] = List(10, 20, 30)
//删除左边的两个元素
scala> info
res43: List[String] = List(lingang, wangfang, changhua, zhangjin, guanyu)
scala> info.drop(2)
res46: List[String] = List(changhua, zhagjin, guanyu)
//删除右边的两个元素
scala> info.dropRight(2)
res47: List[String] = List(lingang, wangfang, changhua)
//反转元素列表
scala> List(10,20,30,40).reverse
res29: List[Int] = List(40, 30, 20, 10)
//转换列表为字符串
/*
mkString函数有两次重载:
(sep: String)String 使用一个分隔符串接列表中的全部元素为一个字符串
(start: String,sep: String,end: String)String 使用一个分隔符串接列表中的全部元素为一个字符串,同时在这个字串的起始和结束位置追加第一个参数字串和最后一个参数字串
*/
scala> List(10,20,30,40).mkString(",")
res39: String = 10,20,30,40
scala> List(10,20,30,40).mkString("(","|",")")
res42: String = (10|20|30|40)

e、列表过滤处理:

scala> val info=List("lingang","wangfang","changhua","zhangjin","guanyu")
info: List[String] = List(lingang, wangfang, changhua, zhangjin, guanyu)
/*
e一、count函数原型是:(p: Any => Boolean)Int,将集合中每个元素放入隐函数计算,并返回集合中知足条件(参数函数返回true)的元素数量 
*/
//使用隐函数计算info集合中元素的字符数为6的元素个数
scala> info.count(s=>6==s.length)
res26: Int = 1
//使用偏函数计算info集合中元素的字符数为6的元素个数
scala> info.count(6==_.length)
res1: Int = 1

/*
e二、exists函数原型是:(p: Any => Boolean)Boolean,只要集合中有一个元素知足条件(参数函数返回true)则exists函数返回true
*/
//判断集合info中是否存在元素的值为changhua的元素
//使用隐函数计算
scala> info.exists(s=>s=="changhua")
res3: Boolean = true
scala> info.exists(s=>s=="changshad")
res4: Boolean = false
//使用偏函数计算
scala> info.exists(_=="changhua")
res6: Boolean = true

/*
e三、forall函数原型是:(p: Any => Boolean)Boolean,集合中全部元素知足条件(参数函数返回true)则exists函数返回true;
该函数与exists函数是互补的,exists函数是描述or的关系,而forall是描述and的关系
*/
//判断集合info中的元素类型是否都是String类型
scala> info.forall(s=>s.getClass().getSimpleName()=="String")
res12: Boolean = true
scala> info.forall(_.getClass().getSimpleName()=="String")
res14: Boolean = true
scala> info.forall(s=>s.getClass().getSimpleName()=="Integer")
res13: Boolean = false
scala> List("lingang","chenghua").forall(_.getClass().getSimpleName()=="String")
res15: Boolean = true
scala> List("lingang","chenghua",50).forall(_.getClass().getSimpleName()=="String")
res16: Boolean = false
scala> List("lingang","chenghua").forall(_.getClass().getName=="java.lang.String")
res17: Boolean = true
scala> List("lingang","chenghua",100).forall(_.getClass().getName=="java.lang.String")
res19: Boolean = false

说明:
在Scala中比较字符串是否相同可使用双等号,固然你使用equals方法来比较也是OK的:
scala> List("lingang","chenghua").forall(_.getClass().getName.equals("java.lang.String"))
res20: Boolean = true

/*
e四、filter函数原型是:(p: Any => Boolean)List[Any],过滤出知足条件(参数函数返回true)的全部元素并返回这些元素组成的子列表
*/
//过滤出字串长度为6的全部元素组成的子列表
scala> info.filter(s=>s.length==6)
res7: List[String] = List(guanyu)
scala> info.filter(_.length==6)
res9: List[String] = List(guanyu)

/*
e五、map函数原型是:(f: Any => B)(implicit bf: scala.collection.generic.CanBuildFrom[List[Any],B,That])That,将集合中的每个元素使用map的参数函数处理并收集参数函数处理后的返回值组成的列表
*/
scala> List(10,20,30,40).map(s=>s+5)
res35: List[Int] = List(15, 25, 35, 45)
scala> List(10,20,30,40).map(_+5)
res37: List[Int] = List(15, 25, 35, 45)

/*
e六、foreach函数原型是:(f: Any => U)Unit,将集合中的每个元素放入参数函数中去并调用参数函数处理,参数函数的返回类型根据参数函数体自动推断,foreach函数无返回值
*/
scala> info.foreach(s=>println(s))
lingang
wangfang
changhua
zhangjin
guanyu
scala> info.foreach(println(_))
lingang
wangfang
changhua
zhangjin
guanyu

J、元素Tuple

  元组与列表相同也是不可改变的,任何对元组的增、删、改都将返回新的元组对象,元组与列表不一样的是列表List中的类型都是相同的,使用列表时为了可以在其中容纳不一样类型的元素,咱们不得不使用与这些元素兼容的超类型,如:AnyVal、AnyRef或Any类型等,可是元组Tuple中的元素类型能够是不相同的,元组Tuple的实际类型取决于元组中元素的数量和各个元素的类型。
a、通用方式建立元组

//元组的泛型被自动推断为Tuple2[String,Int],下面的这条语句存在两次类型推断过程,首先是推断右边Tuple2的泛型为Tuple2[String,Int],而后在推断左边info变量的类型为Tuple2[String,Int]
//TupleX中的X表明元组中元素的数量,元组的泛型由元组中的各个元素类型来肯定,元组中的元素数量和类型决定了元组自身的类型
scala> val info=new Tuple2("liubei",28)
info: (String, Int) = (liubei,28)
//访问元组的第一个元素
scala> info._1
res54: String = liubei
//访问元组的第二个元素
scala> info._2
res55: Int = 28

说明:
  不能像访问List中元素那样去访问元组中的元素,由于List的apply方法返回的类型都是同一类型,然而元组的各个元素的类型不见得彻底相同,访问元组中的元素只能使用成员运算符(.),其中元素格式为_N,N表明元素在元组中的下标

//显式指定元组的泛型,可是左边的info变量的类型须要Scala自动推断
scala> val info=new Tuple2[String,Int]("ligang",28)
info: (String, Int) = (ligang,28)
//显式指定变量的类型和元组的泛型
scala> val info:Tuple2[String,Int]=new Tuple2[String,Int]("ligang",28)
info: (String, Int) = (ligang,28)

b、简洁方式建立元组(无new关键字)

scala> val info=Tuple2("ligang",28)
info: (String, Int) = (ligang,28)
//也能够显式指定泛型和变量的类型
scala> val info=Tuple2[String,Int]("ligang",28)
info: (String, Int) = (ligang,28)
scala> val info:Tuple2[String,Int]=Tuple2[String,Int]("ligang",28)
info: (String, Int) = (ligang,28)

c、最简方式建立元组(无Tuple关键字)

//info变量自动被推断为:Tuple2[String,Int]类型
scala> val info=("ligang",28)
info: (String, Int) = (ligang,28)
//也能够显式为变量指定类型
scala> val info:Tuple2[String,Int]=("ligang",28)
info: (String, Int) = (ligang,28)

小结:
  元组Tuple具备比列表List更高一个层级的不可变型,元组Tuple相对于列表List的优点就是它的元素类型能够是不一样的,然而元组Tuple的缺点是没有像列表List那样丰富的API操做,元组中的数据几乎是固定不变的。

K、Set集合

  在Scala中,Set集合分为可变集合和不可变集合,其中的可变集合与Java中的Set集合是类似的,Set在Scala中被定义为一个Trait,它的具体实现是HashSet,HashSet仍然分为可变的HashSet和不可变的HashSet,全部可变的集合放置于scala.collection.mutable包中,而全部不可变的集合则放置于scala.collection.immutable包中,Set集合中的+=运算符在可变与不可变的具体集合实现中有着不一样的实现,在可变的Set集合中,该运算至关于append,即直接往当前集合中追加元素,而在不可变的Set集合中,该运算至关于+产生的合并运算,这将致使产生新的Set集合对象,同时将合并后的新Set集合对象赋值给左边的变量;Set集合中的元素是自动去重的,即没有重复的元素,这个特征不一样于Array和List;建立任何Set集合(包括HashSet集合)都只能使用简洁模式,不能使用new的方式来建立,今后你会发现规避使用new来建立对象在Scala中是一种更好的通用选择。

//建立一个Set集合,泛型被自动推断为Any类型,左边的set变量被自动推断为Set[Any]类型
scala> val set=Set("ligang",28,1.67)
set: scala.collection.immutable.Set[Any] = Set(ligang, 28, 1.67)
//也能够显式指定泛型和变量类型
scala> val set=Set[Any]("ligang",28,1.67)
set: scala.collection.immutable.Set[Any] = Set(ligang, 28, 1.67)
//追加一个元素到Set集合将返回一个新的Set集合对象
scala> val set02=set+"chenggang"
set02: scala.collection.immutable.Set[Any] = Set(ligang, 28, 1.67, chenggang)
scala> set==set02
res3: Boolean = false
//若是你但愿将产生的新的Set集合对象再赋值回原来的变量,相似于字符串同样的去改变原有变量,可使用+=运算符,因为须要改变原有变量的引用值,因此须要使用var来定义该变量,而不是val
scala> var set=Set("ligang",28,1.67)
set: scala.collection.immutable.Set[Any] = Set(ligang, 28, 1.67)
//下面的这步操做至关于:set=set+"chenggang",这显然是改变了原有set变量的值,这也是上面为何要使用var来定义它的缘由所在
scala> set+="chenglong"
scala> set
res5: scala.collection.immutable.Set[Any] = Set(ligang, 28, 1.67, chenglong)
//再次追加相同的元素则不会追加成功
scala> set+="chenglong"
scala> set
res3: scala.collection.immutable.Set[Any] = Set(ligang, 28, 1.67, chenglong)

说明:
上面的操做也许会考虑到使用val从新定义set变量(而坚持不使用var),但这种状况会发生递归错误,因此咱们只能使用另外一个变量set02来替换,或者使用var定义它而后使用+=运算符

scala> val set=set+"zhangsan"
<console>:12: error: recursive value set needs type
val set=set+"zhangsan"
^
从上面的Set集合建立过程能够看出Set集合默认建立为不可变的集合类型,若是你须要的是可变的集合类型则必须使用import显式的导入可变类型的Set
scala> import scala.collection.mutable.Set
import scala.collection.mutable.Set
scala> val set=Set("ligang",28,1.67)
set: scala.collection.mutable.Set[Any] = Set(ligang, 1.67, 28)
//下面这句至关于append操做,直接改变集合对象自己,并不会产生新的集合对象
scala> set+="zhangsan"
res5: set.type = Set(ligang, 1.67, 28, zhangsan)
//再次追加相同的元素值则不会追加成功
scala> set+="zhangsan"
res6: set.type = Set(ligang, 1.67, 28, zhangsan)

/*
若是你想基于特定的实现类建立Set集合则必须使用import导入相应的类型,但实际生产上用的较少
*/
//导入可变的HashSet类型
scala> import scala.collection.mutable.HashSet
import scala.collection.mutable.HashSet
scala> val set=HashSet("ligang",28,1.67)
set: scala.collection.mutable.HashSet[Any] = Set(ligang, 1.67, 28)
//建立不可变的HashSet类型
scala> val set=scala.collection.immutable.HashSet("ligang",28,1.67)
set: scala.collection.immutable.HashSet[Any] = Set(28, ligang, 1.67)

Set小结:
对于不可变的Set集合类型变量应该使用var来进行定义以保证追加元素后产生的新集合对象可以被赋值回原来的变量,对于可变的Set集合类型一般是使用val来进行定义,此时追加元素将改变集合自己,而不会产生任何新的集合对象。

Array、List、Set、Tuple小结:
尺寸的可变性:
Array和Tuple是在实例化时定义长度的,实例化以后不能够改变它的长度;而List、Set则能够动态改变长度,如List能够经过::追加元素产生新的List对象,而Set能够经过+=追加元素产生新的Set对象或者直接改变集合自己;
集合中元素类型是否相同:
Array、List、Set集合中元素类型相同,Tuple集合中的元素类型能够相同、也能够不一样;
集合中的元素值是否能够改变:
Array集合中的元素值能够改变,List和Tuple集合中的元素值不能被改变,Set集合中的元素有可变和不可变两种实现(分别使用val和var来定义);
元素是否能够重复:
Set集合中的元素不能够重复,Array、List、Tuple集合中的元素值能够重复;
泛型推断原理:
对于不可变集合而言必须在实例化时指定各个元素的值,此时Scala能够自动根据给定的元素值来推断其集合的泛型;而对于可变集合则能够延迟给定各个元素的值,此时的可变集合泛型在实例化时就会被自动推断为Nothing类型,此后须要添加到可变集合的各个元素不得不强制下转型为Nothing,不然将没法添加元素到可变集合中,对于Array以前咱们就已经看到了,如今来看看可变集合Set,它将与Array遇到一样的状况:

scala> import scala.collection.mutable.Set
import scala.collection.mutable.Set

scala> val set=Set()
set: scala.collection.mutable.Set[Nothing] = Set()

scala> set+="ligang"
<console>:14: error: type mismatch;
 found   : String("ligang")
 required: Nothing
       set+="ligang"
            ^

L、Map集合

a、->与<-的区别:
前者是被定义在Any根类中的方法,即任何对象调用该方法都将返回一个二元素元组,该元素中的第一个元素是当前对象,第二个元素是参数对象

scala> "name"->"ligang"
res1: (String, String) = (name,ligang)
scala> "age"->28
res2: (String, Int) = (age,28)
scala> "height"->1.64
res3: (String, Double) = (height,1.64)
scala> 100->"zhanghua"
res4: (Int, String) = (100,zhanghua)
//也可使用成员运算符来调用该方法
scala> 150.->("zhanghua")
res5: (Int, String) = (150,zhanghua)

后者是被用在for循环中遍历集合之用:

scala> val info=List(20,60,80)
info: List[Int] = List(20, 60, 80)
scala> for(e<-info)println(e)
20
60
80

b、建立不可变的Map对象
Map集合中下标被称之为Key,元素值被称之为Value,Map集合中的每个元素被称之为一个键值对的对象,这个键值对是一个包含了Key和Value的二元素元组

//首先建立三个键值对元组对象,它们是entry一、entry2和entry3
scala> val entry1="name"->"ligang"
entry1: (String, String) = (name,ligang)
scala> val entry2="age"->36
entry2: (String, Int) = (age,36)
scala> val entry3="height"->1.67
entry3: (String, Double) = (height,1.67)
//再建立不可变的Map对象,注意对不可变的Map应该使用var来定义,不然后续没法使用+=运算符追加键值对
scala> var info=Map(entry1,entry2,entry3)
info: scala.collection.immutable.Map[String,Any] = Map(name -> ligang, age -> 36, height -> 1.67)
//访问Map集合中的元素应根据Key来访问Value
scala> info("name")
res8: Any = ligang
scala> info("age")
res9: Any = 36
//追加键值对
val entry3="height"->1.67
scala> val info2=info+entry4
info2: scala.collection.immutable.Map[String,Any] = Map(name -> ligang, age -> 36, height -> 1.67, weight -> 118)
scala> info==info2
res13: Boolean = false
//也可使用+=来追加键值对
scala> val entry5="birthday"->java.sql.Date.valueOf("1982-12-26")
entry5: (String, java.sql.Date) = (birthday,1982-12-26)
scala> info+=entry5
scala> info
res15: scala.collection.immutable.Map[String,Any] = Map(name -> liyongfu, age -> 36, height -> 1.67, birthday -> 1992-12-26)

c、建立可变的Map对象

scala> import scala.collection.mutable.Map
import scala.collection.mutable.Map
//注意对可变的Map应该使用val来定义,不然后续没法使用+=运算符追加键值对
scala> val info=Map(entry1,entry2,entry3) info: scala.collection.mutable.Map[String,Any] = Map(age -> 36, name -> ligang, height -> 1.67) scala> info+=entry5 res16: info.type = Map(birthday -> 1992-12-26, age -> 36, name -> ligang, height -> 1.67) scala> info res17: scala.collection.mutable.Map[String,Any] = Map(birthday -> 1992-12-26, age -> 36, name -> ligang, height -> 1.67)

说明:
Scala中的集合类型默认引用的是不可变的集合类型,若是须要使用可变的集合类型则须要手动使用import导入scala.collection.mutable._(这里的下划线至关于java中的*),如:

scala> import scala.collection.mutable._
import scala.collection.mutable._
scala> val info=Map[String,Any]()
info: scala.collection.mutable.Map[String,Any] = Map()
//直接追加键值对
scala> info+="name"->"ligang"
res0: info.type = Map(name -> ligang)
scala> info+="age"->28
res1: info.type = Map(age -> 28, name -> ligang)

在Java中一般建立的集合对象都是可变的,同时若是引用集合对象的变量没有使用final修改则该变量也是可变的,若是在Scala中须要像Java中的那种集合建立方式则应该是以下状况:

scala> import scala.collection.mutable._
import scala.collection.mutable._
scala> var info=Map[String,Any]()
info: scala.collection.mutable.Map[String,Any] = Map()

M、函数式风格编程与文件操做

M一、var与val的选择

  一般状况下,若是使用var定义来实现编程,那么这种风格就是Scala中的指令式风格,指令式风格对于C/C++以及Java程序员都是司空见惯的一种模式,若是须要向函数式风格化推动,则应该尽量的使用val编程,而再也不是使用var来编程;其二,一般咱们在编程中应该首先思考的是Scala的API,当咱们考虑到有难度时再回退到Java的API中来实现,这是一种不错的思路。

M二、文件操做

//构建测试文件

[root@CloudDeskTop install]# vi testfile
[root@CloudDeskTop install]# cat testfile 
01    lingang    2009-12-28
02    zhanghua    1998-10-12
03    chengqiang    1923-11-18
04    欢迎来到mmzs
05    以为有用的话
06    点个赞呗

//编写脚本

[root@CloudDeskTop install]# vi testreadfile
[root@CloudDeskTop install]# cat testreadfile 
import scala.io._
if(args.length<=0){
println("args number is error...")
java.lang.System.exit(1)
}
val fileName=args(0)
val source:BufferedSource=Source.fromFile(fileName)
val its:Iterator[String]=source.getLines
for(line<-its) println(line.length+"=>"+line)

//调用脚本读取文件内容并打印到控制台

[root@CloudDeskTop install]# scala testreadfile testfile
27=>01    lingang    2009-12-28
28=>02    zhanghua    1998-10-12
30=>03    chengqiang    1923-11-18
14=>04    欢迎来到mmzs
12=>05    以为有用的话
10=>06    点个赞呗

说明:
val its:Iterator[String]=source.getLines返回的是一个迭代器,迭代器指针指向磁盘文件的第一行,每循环一次都将从磁盘上读取下一行的数据到内存中;
若是须要一次性将磁盘文件中的内容读取到内存可使用迭代器调用toList方法:
val its:Iterator[String]=source.getLines
val allLines:List[String]=its.toList

//代码以下
[root@CloudDeskTop install]# cat testreadfile 
import scala.io._
if(args.length<=0){
    println("args number is error...")
    java.lang.System.exit(1)
}
val fileName=args(0)
val source:BufferedSource=Source.fromFile(fileName)
val its:Iterator[String]=source.getLines
//for(line<-its) println(line.length+"=>"+line)
//今后行开始修改 val allLines:List[String]=its.toList println(allLines.length+"=>"+allLines) //结果以下 [root@CloudDeskTop install]# scala testreadfile testfile 6=>List(01 lingang 2009-12-28, 02 zhanghua 1998-10-12, 03 chengqiang 1923-11-18, 04 欢迎来到mmzs, 05 以为有用的话, 06 点个赞呗)

 

相关文章
相关标签/搜索