首页 > 技术文章 > scala

alexhjl 2017-12-25 11:23 原文

推荐书籍,快学scala

val str="i love scala"  //编译器自动确定类型,使用val定义的变量值是不可变的,相当于java里用final修饰的变量

val i = 1

var s="hello"    //使用var定义的变量是可变的,再scala中鼓励使用val

val stri: String ="alex"   //scala编译器会自动推断变量的类型,必要的时候可以指定类型,变量名在前,类型在后

常用的变量类型: Byte,Char,Short,Int,Long,Float,Double,Boolean

if例子:val y = if(x >0) 1 else -1

if例子:val z=if(x>0) 1 else "error"  //在未赋值时,可以是数值,也可以是字符串。但是他的类型变成了"Any"

if例子:val m =if(x >2) 1   //如果x不大于2,那么是未赋值。返回值是m: AnyVal=()。在scala中每个表达式都有值,scala仲有个Unit类,写做(),相当于java中的void

object 单例对象

class 类

trait 相当于java中的接口

块表达式:

object BlockExpressionDemo {
def main(args: Array[String]): Unit = {
val x=0
val result={ //这是一个块表达式,块中最后一个表达式的值就是块的值
if(x<0){
-1
}else if(x>=1){
1
}else {
"error"
}
}
  println(result);
}
}

 

数组例子:

val arr=Array(1,2,3)

val arr2=(1 to 10)

val arr3=Array(1 to 10)   //这样是错的,

arr(1)=100

 

for循环:

1 to 10  //会返回一个scala的Range(1,2,3,4,5,6,7,8,9,10)

for(i <- 1 to 10){
println(i)
}
val a1=(1 to 9)
for(i <- 0 until a1.length) println(i) //打印出来的0-8

高级for循环: //注意:if前面没有分号

for(i<- 1 to 3; j <- 1 to 3 if i!=j){
print((10 * i + j )+" ")
}

for推导式

val v = for (i <- 1 to 10) yield i*10    //yield生成一个新的集合数组
println(v)//会发现v是一个新集合 Vector(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)

map例子:

val z=(1 to 10).map(_*10)  //也可以写成 (1.to(10).map(_*10),也是返回一个Vector数组
println(z)
val f1=(x:Int) => x * 10
val r= 1 to 10
r.map(f1)

filter例子:

val a1=(1.to(9))
val a2=a1.filter(_%2==0) //filter也是返回一个新数组

until方法:

val a1=(1 to 9)
0 util a1.legnth
for(i <- 0 until a1.length) println(i) //打印出来的0-8

 

定义方法和函数,函数和方法是有区别的,函数可以作为一个值传到方法里

方法:

def m1(x: Int, y: Int) : Int = x * y      //x: Int 参数列表    : Int 方法返回值类型  方法的返回值类型可以不写,编译器可以自动推断,但是对于递归函数,必须指定返回类型

调用val r=m1(3,5)

 

定义没有返回值的方法:

def m2(name:String): Unit ={
  println(name)
}
val v=m2("alex")
print(v)

 

匿名函数:

val abc=(x:Int,y:Int) => x+y   

val func=(x:Int,y:Double) => (y,x) //返回值是一个元组

val func2:(Int,Double) => (Double,Int) = {(a,b) => (b,a) }   //(a,b)代表前面(Int,Double)的局部变量   和上面例子的效果一样

简单版匿名函数传入方法的例子:

val f1=(x:Int) => x * 10
val f2=(x:Int) => x+10
val r= 1 to 10
r.map(f1)
r.map(f2)
r.map((x:Int) => x*100) //直接定义一个匿名函数
r.map(x => x*100) //同上面一样效果,更简洁
r.map(_*100) //同上面一样效果,最简洁
val arr=1 to 9
val r1=arr.map(x=>x*5) //产生一个新数组r1
val r2=arr.map(x=>x-1) //产生一个新数组r2
println(r1) //打印数组
println(r1.toBuffer) //把r1变成一个ArrayBuffer

完整版匿名函数传入方法的例子:

object HelloScala{
def main(args: Array[String]): Unit = {
println(m1(func))
}
val func = (x:Int) => x*10
def m1(f:Int => Int) : Int={ //表示需要传入一个Int参数,返回一个Int值,
//在方法体里调用函数
f(3)
}
}

高级版定义匿名函数的例子:

 val func:Int  =>  String  = { x => x.toString }  //传入一个Int     =>是返回值,返回一个String   后面必须有大括号函数体    要有一个变量x接收传进来Int,这个x是一个局部变量

调用func(10)    就返回一个String

 

将方法转换成函数(神奇的下划线)

def m2(x:Int,y:Int) :Int =x+y  //定义一个方法

scala> def m2(x:Int,y:Int) :Int =x+y 

m2: (x: Int, y: Int)Int

val f2=(a:Int,b:Int) => a+b   //定义一个函数

val f3=m2 _    //把方法m2变成了函数

 

数组,映射,元组,集合

数组:定长数组和变长数组

val arr=new Array[Int](10)    //装Int的数组,长度为10,里面值是0

println(arr)   //直接打印定长数组,内容为数组的hashcode值

println(arr.toBuffer)  //将数组转换成数组缓冲,就可以看到原数组中的内容了,toBuffer会将数组转换成数组缓冲

arr(0)=1   //赋值

arr(1)=2

val arr1=Array(1,3,4,5,6)   //定义一个长度为5的数组

val arr2=Array[Int](10)   //这里没new,这样只创建了一个长度为1的数组,里面只有一个值为10

println(arr2.toBuffer)  //ArrayBuffer(10),长度为1的数组,里面只有一个值为10

 

变长数组(数组缓冲):

ArrayBuffer,如果想使用数组缓冲,需要导入import scala.collection.mutable.ArrayBuffer包

val ab=ArrayBuffer[Int]()

ab+=1

ab+=(2,3,4,5) //追加多个元素

ab++=Array(6,7)  //追加一个数组用两个加号 ++=

ab++=ArrayBuffer(8,9)   //追加一个数组缓冲

ab.insert(0,-1,0)   //在数组某个位置插入元素用insert  ,这里在数组的0下标上插入了两个数字-1和0

ab.remove(8,2)  //删除数组某个位置的元素用remove,这里删除了下表8开始的2个元素

 

遍历数组:

1.增强for循环 

val arr=Array(1,2,3,4,5,6)

for(i <- arr)

  println(i)

2.好用的uttil会生成脚标,0 until 10  包含0不包含10

for(i <- (0 until arr.length).reverse)   //reverse反转

  println(arr(i))

 

val a1=ArrayBuffer(8,2,3)

a1.map(print)

a1.map(println)

a1.map(println(_))

 

数组的转换:

yield关键字将原始的数组进行转换会产生一个新的数组

val a =Array(1,2,3,4,5)

for(i<- a) yield i * 10

a.map(x=>x*10) //效果和上面相同

a.map((x:Int)=>x * 10)  //效果和上面相同

a.map( _ * 10)   //效果和上面相同

 

数组转换小例子:

val arr=Array(1,2,3,4,5,6,7,8,9)

//将偶数取出,再乘以10,生成一个新的数组

val res=for (e <- arr if e%2==0) yield e*10   //第一种方法

arr.filter((x:Int) => x%2 ==0 )     //传进来一个Int,返回一个布尔值  , 最后结果是2,4,6,8

arr.filter(x=> x %2 ==0)        //上面的简单版,最后结果是2,4,6,8

arr.filter( _ % 2 ==0)    //更简洁版,最后是2,4,6,8

arr.filter( _ % 2 ==0).map( _ * 10)    //正确答案

 

其他例子:arr.sum    //返回值45

val arr=Array(2,5,1,4,7,3)

arr.sorted   //返回值1,2,3,4,5,6,7

arr.sorted.reverse   //返回值7,6,5,4,3,2,1

arr.sortBy(x => x)   //返回值1,2,3,4,5,6,7

arr.sortWith( _>_ )   //返回值7,6,5,4,3,2,1 ,从大到小

arr.sortWith( _<_ )   //返回值1,2,3,4,5,6,7,从小到大

arr.sortWith((x,y) => x< y )   //返回值1,2,3,4,5,6,7

 

映射:

构建映射:

val m=Map("a" -> 1, "b" -> 2)   //创建的时候是immutable.Map  不可改变

m("a")  //访问

m("a") =2   //报错,不可改变,

import scala.collection.mutable.Map

val mm=Map("i"->1, "j" ->2)  //创建的是mutable的map,可以改变

mm("i")=10    //可以

mm("k")=20 //可以

mm+=("o" -> 6) //可以

mm+=(("o",6))  //可以

import java.util.HashMap   //可以引入java的包

val hm=  new HashMap()

 

val m = Map(("a",1) ,("b" ,2))    //其实映射里装的是元组,都是对偶元组

访问映射:

m.getOrElse("c",0)    //如果m里面没有c,不会报错 ,给他个0

 

元组:

val t=(1,"spark",2.0)  //返回一个(Int, String, Double)

访问:

val r=t._2   //取出spark

 

val pair=("t",5)  //定义一个对偶元组

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

把上面的pari装入m

m+= pair   //res5: m.type = Map(t -> 5, b -> 2, a -> 1)

m+=(("y",6),("z",3))  // res6: m.type = Map(z -> 3, t -> 5, b -> 2, y -> 6, a -> 1)

 

特殊的例子:

val t=("a",  1,  2.0) 

t._2

res0: Int = 1

scala> t._1

res1: String = a

val t,(x,y,z)=("a",1, 2.0)

t: (String, Int, Double) = (a,1,2.0)

x: String = a

y: Int = 1

z: Double = 2.0

scala> x

res2: String = a

 将对偶的集合转换成映射:

scala> val arr=Array(("a",1),("b",2))

arr: Array[(String, Int)] = Array((a,1), (b,2))

scala> arr.toMap    //转换成了map

res3: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2)

 

拉链操作:

scala> val a=Array("a","b","c")

a: Array[String] = Array(a, b, c)

scala> val b=Array(1,2,3)

b: Array[Int] = Array(1, 2, 3)

scala> a.zip(b)

res4: Array[(String, Int)] = Array((a,1), (b,2), (c,3))

如果拉链多出来一截:

scala> val b=Array(1,2,3,4)

b: Array[Int] = Array(1, 2, 3, 4)

scala> a.zip(b)

res5: Array[(String, Int)] = Array((a,1), (b,2), (c,3))   //多出来的一截自动没有

 

集合:

集合有三大类:序列Seq,集Set,映射Map,所有集合都扩展子Iterable特质。集合有可变和不可变的两种类型。

val lst=List(1,3,4,5,6)

val list=List(1,2,3)

list(0)   //取值

list(0)=10  //报错,不可变

导包:

import scala.collection.mutable.ListBuffer

val lb=ListBuffer(1,2,3)

lb(1)=200 //可以改变值了

scala> lb

res7: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 200, 3)

scala> lb.map(_ * 10)   //新生成一个List

res8: scala.collection.mutable.ListBuffer[Int] = ListBuffer(10, 2000, 30)

val lst1=List(1,2,3)

val lst2=0::lst1  //将0插入到lst1的前面生成一个新的List

val lst3=lst1.::(0)  //效果同上

val lst4=0+:lst1  //效果同上

val lst5=lst1.+:(0)  //效果同上

val lst6=lst1 :+3   //将一个元素添加到lst1的后面产生一个新的集合

 

val lst0=ListBuffer[Int](1,2,3)

val lst1 = new ListBuffer[Int]

lst1 +=4   //追加4

lst1.append(5)   //追加5

lst1 +=(8,9)  //追加多个元素

lst2 = ListBuffer(10)   //创建一个新集合

lst1 ++=lst2  //把lst2加到lst1里  和 ArrayBuffer里面一样

val lst3=lst1++lst2  //两集合相加,并生成一个新集合

List小例子:

val lst=List(1,3,2,7,5,9,6,4,8)   //创建一个List

val lst1=lst.sorted   //从大到小排序

val it = lst1.grouped(5)   //创建了一个iterator

val lst2=it.toList   //查看it里的两个集合,将iterator转换成List, lst2里有两个List,大List套了两个小List

lst2(0)   //访问第一个list

val lst3=lst2.flatten    //把lst2中的两个小list,平铺成一个大List

 

例子:单机版WordCount

val lines =List("hello tom hello jerry","hello tom kitty hello hello")

lines.map(_.split(" "))   //用空格来切 ,结果是res35: List[Array[String]] = List(Array(hello, tom, hello, jerry), Array(hello, tom, kitty, hello, hello))

lines.map(_.split(" ")).flatten    //上面切完以后,用flatten,编程一个大集合  res36: List[String] = List(hello, tom, hello, jerry, hello, tom, kitty, hello, hello)

 

//比上面更好的方法

val words=lines.flatMap(_.split(" "))      //flatMap想当与先做了map再做flat操作。

val wordsAndOne = words.map((_,1))    //返回一个List,list里装的是对偶元组。wordsAndOne: List[(String, Int)] = List((hello,1), (tom,1), (hello,1), (jerry,1), (hello,1), (tom,1), (kitty,1), (hello,1), (hello,1))

val grouped = wordsAndOne.groupBy(_._1)   //第一个_ 代表每个元组例如(hello,1),   第二个_1代表每个元组里的第一个元素,例如hello。   grouped: scala.collection.immutable.Map[String,List[(String, Int)]] = Map(tom -> List((tom,1), (tom,1)), kitty -> List((kitty,1)), jerry -> List((jerry,1)), hello -> List((hello,1), (hello,1), (hello,1), (hello,1), (hello,1)))

//不用执行,可以看看 val result =grouped.map(_._1)               //返回一个List。请比较一下val result =grouped.map(_._2) 的区别。 还有,请试试val result=grouped.keys 这个返回是一个Set(tom,kitty,jerry,hello)

result: scala.collection.immutable.Iterable[String] = List(tom, kitty, jerry, hello)

val result = grouped.map(t => (t._1,t._2.size))      //t是对偶元组tom->List((tom,1),(tom,1))     t._1是key,就是tom,  t._2是List,然后取他的size。 最后结果result: scala.collection.immutable.Map[String,Int] = Map(tom -> 2, kitty -> 1, jerry -> 1, hello -> 5)

val finalResult = result.toList.sortBy(_._2)   //  _是元组,按照次数排序。升序finalResult: List[(String, Int)] = List((kitty,1), (jerry,1), (tom,2), (hello,5))

val finalResult = result.toList.sortBy(_._2).reverse  //降序

val finalResult = result.toList.sortBy(_._2).reverse.filter(_._2>1)   //过滤1次以上的数据

finalResult: List[(String, Int)] = List((hello,5), (tom,2))

 

//上面的简化版

val lines =List("hello tom hello jerry","hello tom kitty hello hello")

val grouped = lines.flatMap(_.split(" ")).map((_,1)).groupBy(_._1) 

val result=grouped.mapValues(_.size)     //  _代表每个List,例如List((tom,1),(tom,1))    mapValues不动key,所以还是tom,kitty,jerry等,统计的是values,也就是每个List

 

fold例子:

val a=Array(1,2,3,4,5,6)

a.sum   //21

a.reduce (_+_)    //reduce把里面内容进行汇总 ,结果21     (((1+2)+3)+4)  从左开始加    默认调用reduceLeft

a.reduce(_-_)   //-19

a.par   //返回一个可以并行化操作的集合res0: scala.collection.parallel.mutable.ParArray[Int] = ParArray(1, 2, 3, 4, 5, 6)

a.par.reduce(_+_)   //21  

fold(10)(_+_)   //蜕化。可以传2个参数。

a.fold(10)(_+_)    //31  给了个初始值10,

fold也支持并行化计算

a.par.fold(10)(_+_)   //61

 

Set:

不可变的Set: import scala.collection.immutable.HashSet

val set1 = new HashSet[Int]()

  //将元素和set1合并生成一个新的set,原有set不变

val set2 = set1 + 4

      //set中元素不能重复

val set3 = set1 ++ Set(5, 6, 7)

val set0 = Set(1,3,4) ++ set1

println(set0.getClass)

            

 可变的Set: 

import scala.collection.mutable

 

object MutSetDemo extends App{

      //创建一个可变的HashSet

        val set1 = new mutable.HashSet[Int]()

          //向HashSet中添加元素

            set1 += 2

              //add等价于+=

                set1.add(4)

                  set1 ++= Set(1,3,5)

                    println(set1)

                      //删除一个元素

                        set1 -= 5    

                          set1.remove(2)

                            println(set1)

}

 

Map:也分为可变和不可变

object MutMapDemo extends App{

      val map1 = new mutable.HashMap[String, Int]()

        //向map中添加数据

          map1("spark") = 1

            map1 += (("hadoop", 2))

              map1.put("storm", 3)

                println(map1)

 

                  //从map中移除元素

                    map1 -= "spark"

                      map1.remove("hadoop")

                        println(map1)

}

 

object伴生对象,里面一般存放静态对象,类似于java中的静态变量,静态方法。

 

 

scala的hello world

object HelloScala {   //object 伴生对象,单例对象

  def main(args: Array[String]) {   //所有定义在object里的方法,都是静态方法,相当于java中所有方法加上了static

    println("hello scala,I love u")    

  }

}

 

 

scala里的类

//在Scala中,类并不用声明为public。

//Scala源文件中可以包含多个类,所有这些类都具有公有可见性。

class Person {

      //用val修饰的变量是只读属性,有getter但没有setter

        //(相当与Java中用final修饰的变量)

          val id = "9527"

 

            //用var修饰的变量既有getter又有setter

              var age: Int = 18

 

                //类私有字段,只能在类的内部使用

                  private var name: String = "唐伯虎"

 

                    //对象私有字段,访问权限更加严格的,Person类的方法只能访问到当前对象的>字段

                      private[this] val pet = "小强"

}

 

构造器:

注意:主构造器会执行类定义中的所有语句

/**
*每个类都有主构造器,主构造器的参数直接放置类名后面,与类交织在一起
*/
class Student(val name: String, val age: Int){
//主构造器会执行类定义中的所有语句
println("执行主构造器")

try {
println("读取文件")
throw new IOException("io exception")
} catch {
case e: NullPointerException => println("打印异常Exception : " + e)
case e: IOException => println("打印异常Exception : " + e)
} finally {
println("执行finally部分")
}

private var gender = "male"

//用this关键字定义辅助构造器
def this(name: String, age: Int, gender: String){
//每个辅助构造器必须以主构造器或其他的辅助构造器的调用开始
this(name, age)
println("执行辅助构造器")
this.gender = gender
}
}

 

/**
*构造器参数可以不带val或var,如果不带val或var的参数至少被一个方法所使用,
*那么它将会被提升为字段
*/
//在类名后面加private就变成了私有的
class Queen private(val name: String, prop: Array[String], private var age: Int = 18){

println(prop.size)

//prop被下面的方法使用后,prop就变成了不可变得对象私有字段,等同于private[this] val prop
//如果没有被方法使用该参数将不被保存为字段,仅仅是一个可以被主构造器中的代码访问的普通参数
def description = name + " is " + age + " years old with " + prop.toBuffer
}

object Queen{
def main(args: Array[String]) {
//私有的构造器,只有在其伴生对象中使用
val q = new Queen("hatano", Array("蜡烛", "皮鞭"), 20)
println(q.description())
}
}

 

6.2.1.  单例对象
在Scala中没有静态方法和静态字段,但是可以使用object这个语法结构来达到同样的目的
1.存放工具方法和常量
2.高效共享单个不可变的实例
3.单例模式

ackage cn.itcast.scala

import scala.collection.mutable.ArrayBuffer

/**
* Created by ZX on 2015/11/14.
*/
object SingletonDemo {
def main(args: Array[String]) {
//单例对象,不需要new,用【类名.方法】调用对象中的方法
val session = SessionFactory.getSession()
println(session)
}
}

object SessionFactory{
//该部分相当于java中的静态块
var counts = 5
val sessions = new ArrayBuffer[Session]()
while(counts > 0){
sessions += new Session
counts -= 1
}

//在object中的方法相当于java中的静态方法
def getSession(): Session ={
sessions.remove(0)
}
}

class Session{

}

6.2.2. 伴生对象
在Scala的类中,与类名相同的对象叫做伴生对象,类和伴生对象之间可以相互访问私有的方法和属性
package cn.itcast.scala

/**
* Created by ZX on 2015/11/14.
*/
class Dog {
val id = 1
private var name = "itcast"

def printName(): Unit ={
//在Dog类中可以访问伴生对象Dog的私有属性
println(Dog.CONSTANT + name )
}
}

/**
* 伴生对象
*/
object Dog {

//伴生对象中的私有属性
private val CONSTANT = "汪汪汪 : "

def main(args: Array[String]) {
val p = new Dog
//访问私有的字段name
p.name = "123"
p.printName()
}
}

6.2.3. apply方法
通常我们会在类的伴生对象中定义apply方法,当遇到类名(参数1,...参数n)时apply方法会被调用
package cn.itcast.scala

/**
* Created by ZX on 2015/11/14.
*/
object ApplyDemo {
def main(args: Array[String]) {
//调用了Array伴生对象的apply方法
//def apply(x: Int, xs: Int*): Array[Int]
//arr1中只有一个元素5
val arr1 = Array(5)
println(arr1.toBuffer)

//new了一个长度为5的array,数组里面包含5个null
var arr2 = new Array(5)
}
}


6.2.4. 应用程序对象
Scala程序都必须从一个对象的main方法开始,可以通过扩展App特质,不写main方法。

package cn.itcast.scala

/**
* Created by ZX on 2015/11/14.
*/
object AppObjectDemo extends App{
//不用写main方法
println("I love you Scala")
}

 

 

 

推荐阅读