Scala Language

文章目录

Scala介绍

Scala是一门多范式的编程语言,同时支持面向对象面向函数编程风格。它以一种优雅的方式解决现实问题。虽然它是强静态类型的编程语言,但是它强大的类型推断能力,使其看起来就像是一个动态编程语言一样。Scala语言最终会被翻译成java字节码文件,可以无缝的和JVM集成,并且可以使用Scala调用java的代码库。除了Scala编程语言自身的特性以外,目前比较流行的Spark计算框架也是使用Scala语言编写。Spark 和 Scala 能够紧密集成,例如 使用Scala语言操作大数据集合的时候,用户可以像是在操作本地数据集那样简单操作Spark上的分布式数据集-RDD(这个概念是Spark 批处理的核心术语),继而简化大数据集的处理难度,简化开发步骤。

编程指南:https://docs.scala-lang.org/tour/tour-of-scala.html
函数式编程强调的是通过传递算子(代码|函数)实现大规模数据集的本地计算。

环境配置

scala版本选择页面:https://www.scala-lang.org/download/all.html

Windows版本安装

  • 点击 scala-2.11.12.msi双击msi文件安装
  • 配置Scala的环境变量SCALA_HOME变量\

SCALA_HOME=scala安装目录
PATH=scala安装目录/bin;

打开CMD窗口,输入scala,检查是否安装成功

C:\Users\Administrator>scala
Welcome to Scala 2.11.12 (Java HotSpot™ 64-Bit Server VM, Java 1.8.0_161).
Type in expressions for evaluation. Or try :help.

scala>

CentOS安装

  • 下载 scala-2.11.12.rpm
  • 安装配置Scala

[root@CentOS ~]# rpm -ivh scala-2.11.12.rpm
Preparing… ########################################### [100%]
1:scala ########################################### [100%]
[root@CentOS ~]# scala
Welcome to Scala 2.11.12 (Java HotSpot™ 64-Bit Server VM, Java 1.8.0_171).
Type in expressions for evaluation. Or try :help.
scala>

IDEA集成Scala开发环境

在File>Setting> Plugins直接搜索Scala,安装重启idea即可。创建scala项目时有可能要指定一下本地安装的scala路径

常见变量

在这里插入图片描述
在Java中常见的基本类型在Scala中都被剔除了,Scala将值类型引用类型分离。所有的数值变量类型都是AnyVal的子类,这些变量的都有字面值。 对于一些对象类型的变量都是AnyRef的子类。对于 AnyRef类下的变量(除String类型),一般不允许直接赋值字面量,都需要借助 new关键创建。

变量声明

Scala语言是一种可以做类型自动推断的强类型的编程语言。变量的类型可通过编译器在编译的时候推断出最终类型。因此Scala中声明一个变量主需要告知编译器该变量的值是常量还是变量,例如:例如声明一个变量使用var即可,如果声明的是一个常量使用val关键字。因此Scala中变量声明的语法如下:

Java变量声明
变量类型 变量名= 变量值;        例如 int i = 0;

Scala变量声明
var/val 变量名[:变量类型] = 变量值[:变量类型]

val:不可重新赋值,等价于java中的final
var可以重新赋值
正常情况下用户可以省略类型说明,因为Scala可以根据字面量自动推断出变量的类型,所以在定义一个变量的时候用户只需指明该变量时变量还是常量即可。

数值转换

  • 类型兼容且 由小到大,由整数到小数,可以自动类型提升
scala> var i:Byte = 1
i: Byte = 1

scala> var j:Byte = 1
j: Byte = 1

scala> var k = i+j
k: Int = 2

scala> var m = k + 0.5
m: Double = 2.5

scala> var f:Float=m
<console>:12: error: type mismatch;
 found   : Double
 required: Float
       var f:Float=m
  • 类型兼容,且由大到小 asInstanceOf
scala> var d:Double = 100.0
d: Double = 100.0

scala> var f:Float = d
<console>:12: error: type mismatch;
 found   : Double
 required: Float
       var f:Float = d
                     ^
scala> var f:Float = d.asInstanceOf[Float]
f: Float = 100.0

在Scala中[]表示的是泛型;Scala中没有++、--操作符,但有+=、-=

  • 将字符串类型转为数值类型
scala> var sex=true
sex: Boolean = true

scala> sex="false"
<console>:12: error: type mismatch;
 found   : String("false")
 required: Boolean
       sex="false"
           ^

scala> sex="false".toBoolean
sex: Boolean = false

scala> var i=1
i: Int = 1

scala> i ="123".toInt
i: Int = 123

Unit类型

在Scala中通常会使用Unit关键字表示一种特殊的返回值类型,等价于Java中void关键字。但是在Scala中Unit是有字面量的 ()。

scala> var u:Unit=()
u: Unit = ()

在这里插入图片描述
在这里插入图片描述

== 和===的差异

区别
1.===:三个等号我们称为等同符,当等号两边的值为相同类型的时候,直接比较等号两边的值,值相同则返回true,若等号两边的值类型不同时直接返回false。

例:100===“100” //返回false

2.==:两个等号我们称为等值符,当等号两边的值为相同类型时比较值是否相同,类型不同时会发生类型的自动转换,转换为相同的类型后再作比较。

类型转换规则:

  1. 如果等号两边是boolean、string、number三者中任意两者进行比较时,优先转换为数字进行比较。
  2. 如果等号两边出现了null或undefined,null和undefined除了和自己相等,就彼此相等

例:100==“100” //返回true

数组

scala> var a:Array[Int] = new Array[Int](5) # 直接指定数组大小,并且赋值默认值
a: Array[Int] = Array(0, 0, 0, 0, 0)

scala> var a:Array[Int]=Array(1,2,3,4,5) # 一种快速创建复杂对象方式,后面会讲解(伴生对象)
a: Array[Int] = Array(1, 2, 3, 4, 5)

scala> a.length // 获取数组长度
res6: Int = 5

scala> a.size  // 获取数组长度
res7: Int = 5

scala> a(0) = -1 // 修改数组内容

scala> a
res9: Array[Int] = Array(-1, 2, 3, 4, 5)

scala> a(5) = -1 // 数组越界
java.lang.ArrayIndexOutOfBoundsException: 5
  ... 32 elided

元组(Tuple)

元组是在Scala编程语言中的一种特殊变量,该变量可以组合任意几种类型的变量,构建一个只读的变量类型。访问元组的成员可以使用_元素下标访问。

scala> var user = ("zhangsan",18,true,15000)
user: (String, Int, Boolean, Int) = (zhangsan,18,true,15000)

scala> var a:(String,Int)=("hello",1)
a: (String, Int) = (hello,1)

scala> user._1  #访问元组中第几个元素,下标从1开始,最多到
res12: String = zhangsan

scala> user._1 = "wangwu" #元组中的元素都是val,不允许修改
<console>:12: error: reassignment to val
       user._1 = "wangwu"
               ^

元组中元素都是只读的,一般有值类型和String类型构成。在Scala一个Tuple最多能够有22个成员。元组中的元素都是val类型不允许修改(只读),但是元组变量可以修改

scala> var user = ("zhangsan",18,true,15000)
user: (String, Int, Boolean, Int) = (zhangsan,18,true,15000)

scala> user = ("wangwu",20,false,20000)
user: (String, Int, Boolean, Int) = (wangwu,20,false,20000)

分支循环

条件分支 if-else

if分支语法和Java语言中的语法基本保持一致,语法如下:

if(条件){
  
}else if(条件){

}
...
else{

}

和Java不同,if可以将修饰的代码块的返回值返回给一个变量。

var a=28
var result=if(a<=10){
    "child"
}else if(a<=20){
    "boy|girl"
}else{
    "man|woman"
}
println(s"结果:${result}")
while、do-while

while/do-while循环控制和Java语言中的语法基本保持一致,语法如下:

while(条件){
	//循环体
}

do{
	//循环体
}while(条件)

在scala中没有continue和break关键字。

Breaks

Scala 语言中默认是没有 break 语句,但是在 Scala 2.8 版本后可以使用另外一种方式来实现 break 语句。当在循环中使用 break 语句,在执行到该语句时,就会中断循环并执行循环体之后的代码块。

var arr:Array[Int] = Array(1,2,3,4,5);
//这里提前使用了for循环
var break=new Breaks
break.breakable({
  for (i <-arr){
	if (i==3){
	  break.break();// i=3时,跳出循环,相当于break;打印结果 1和2
	}else{
	  println(i)
	}
  }
})

for (j<-arr){
  break.breakable({
	if (j==3){
	  break.break();//等于3时跳过,相当于continue;打印结果1,2,4,5
	}else{
	  println(j)
	}
  })
}
for循环
  • 迭代遍历数组
var arr:Array[Int] = Array(1,2,3,4,5);
for (i <- arr) {
      println(i)
    }
  • 通过下标遍历数组
//until:最大值为arr.length-1
for (i <-0 until arr.length){
      println(arr(i))
    }
 //to:会取到数组最大值,所以要减1,否则会数组越界
 for (i <-0 to arr.length-1){
      println(arr(i))
    }   
  • for可使用多个循环因子
//打印99乘法表
for (i <- 1 to 9; j <- 1 to i) {
  print(s"${i}*${j}=" + i * j + "\t")
  if (i == j) {
	println()
  }
}
  • for和if的使用
for (i<- 0 until(10) ;if(i%2==0)){
  println(i)
}
yield

当我们使用for或者if,或者for与if联合使用,会过滤出符合你条件的集合,但是当你并不需要打印过滤出来的集合时,你需要对过滤后的集合进行处理,使用yield关键字可以在for表达式中生成新的集合。

val fruits = List("banana fruit","apple fruit","peach","grapes","pear")

val filterFruit = for {
    fruit <- fruits
    if fruit.contains("fruit") && !fruit.startsWith("banana")
}yield fruit

结果为:
fruits: List[String] = List(banana fruit, apple fruit, peach, grapes, pear)
filterFruit: List[String] = List(apple fruit)
match-case(模式匹配)

作用和java中的switch-case一样,match-case不仅仅可以按照值匹配,还可以按照类型、以及值得结构(数组匹配、元组匹配、case-class匹配等.).

//值匹配
var a=Array(1,2,3)
var i=a(new Random().nextInt(3))
var res= i match {
case 1 => "one"
case 2 => "two"
case 3 => "three"
case default => null
}
println(res)

//类型匹配
var a=Array(100,"张三",true,new Date())
var i=a(new Random().nextInt(4))
var res= i match {
    case x:Int => s"age:${x}"
    case x:String => s"name:${x}"
    case x:Boolean => s"sex:${x}"
    case _ => "啥都不是"
}
println(res)

注意:_表示默认匹配等价default关键字

函数

函数声明
def 函数名(参数:参数类型,...):[返回值类型] = {
    //方法实现
}

//标准函数
def sum(x:Int,y:Int):Int = {
    return x+y
}

//可以省略返回值类型
def sum02(x:Int,y:Int)= {
    x+y
}
//可以省略return关键字以及返回值类型,系统一般可以自动推断。
可变长参数
def sum(args:Int*):Int={
    var sum:Int=0
    for(agr <- args){
        sum+=agr
    }
    return sum
}
注意:可变长参数必须放置在最后(如果有多个参数),因为要放在最后,所以同一个方法中不可能存在多个可变长参数
命名参数
def sayHello(msg:String,name:String):Unit={
    println(s"${msg}~ ${name}")
}
//调用
sayHello("hello","张三")
sayHello(name="张三",msg="hello")
参数默认值
def sayHello02(msg:String="哈喽",name:String):Unit={
    println(s"${msg}~ ${name}")
}
def sayHello03(name:String,msg:String="哈喽"):Unit={
    println(s"${msg}~ ${name}")
}
//其实相当于java中接收参数时的@RequestParam(defaultValue = "111")的defaultValue 

//调用
sayHello02(name="张三")	//结果:哈喽~张三
sayHello03("李四")	//结果:李四~哈喽
内嵌函数
object Test {
    def main(args: Array[String]) {
        var res = factorial(5)
        println(res)
    }
    //计算阶乘
    def factorial(x:Int)={
    	//factorial中内嵌了一个函数mulit,和java的内部类有相似之处
        def mulit(i:Int):Int={ //该函数存在递归,必须申明返回值类型
            if(i > 0){
                i*mulit(i-1)
            }else{
                1
            }
        }
        mulit(x)
    }
}
柯里化(Currying)

在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接收一个单一参数的函数,并且返回接收余下的参数且返回结果的新函数的技术。

def main(args: Array[String]) {
 println(sum1(1)(2))
}
//正常函数
def sum(x:Int,y:Int):Int={
 return x+y
}
//科里化
def sum1(x:Int)(y:Int)(z:Int):Int={
 return x+y+z
}
scala> sum1(1)(2)(_)
res19: Int => Int = <function1>
scala> res19(3)
res20: Int = 6

//科里化
def sum2(x:Int)=(y:Int)=>{
   //这里(y:Int)是个函数
   x+y
}

未来用户可以只传递部分参数,返回新的函数

匿名函数

Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。使用匿名函数后,我们的代码变得更简洁了。

(x:Int,y:Int) => {x+y}

//第一种写法,也是相当常见的
//a1的类型为(Int,Int)=>Int,因为scala会自己推算类型,所以省略不写
var a1 = (x:Int,y:Int)=>{if (x>2){
      x+y;
    }else{
      x-y;
    }
}
//第二种写法,返回值类型不省略,参数类型省略
var a2:(Int,Int)=>Int=(x,y)=>{x*y}

var multi=(x:Int,y:Int)=> {x*y}
println(a1(1,2))	//结果:-1
println(a1(3,4))	//结果:7

Class & object

由于Scala没有静态方法和静态类,通过object去定义静态方法或者静态对象(单例对象)。当object和Class放在一个文件中时候称该object为当前Class的伴生对象。

单例类

单例类使用object修饰,所有声明在object中的方法都是静态方法,类似于Java中声明的工具类的作用。

object SingleObject {
    def sum(x:Int,y:Int):Int={
      x+y
    }
}

var so1=SingleObject
var so2=SingleObject

println(so1==so2) //true,因为是单例对象
SingleObject.sum(1,2)
类和构造器this
  • 正常创建
class User {
  var id:Int =_
  var name:String = _
  var age:Int = _
  //构造器,就是java中的构造方法
  def this(id:Int,name:String,age:Int){
    this()
    this.id=id
    this.name=name
    this.age=age
  }
}

必须要求在构造方法的第一行显式调用this(),其中_表示参数赋值为默认值

class User {
  var id:Int =_
  var name:String = _
  var age:Int = _
  def this(age:Int){
    this()
    this.age=age
  }
  def this(id:Int,name:String,age:Int){
    this(age)	//这里调用的this构造函数中显式调用了this()
    this.name=name
    this.id=id
  }
}
  • 类上声明(默认构造器)
class User(var id:Int,var name:String){     //默认构造器
  var salary:Double = _

  def this(id:Int,name:String,salary:Double){//扩展构造器
    this(id,name)
    this.salary=salary
  }
}

注意扩展构造器的第一行必须调用默认构造器_表示默认值

伴生对象

如果类和object在一个scala文件中,则称为object User 是class User的伴生对象,使用伴生对象可以方便的创建对象,只需要覆盖对应的apply方法。

class User(id:Int,name:String,age:Int) {
  override def toString: String = {
    this.id+" "+this.name+" "+this.age
  }
}
object User{
  def apply(id: Int, name: String, age: Int): User = new User(id, name, age)
}

var user=User(1,“zhangsan”,18)//等价 new User(1,“zhangsan”,18)
这里可以理解apply是一个工厂方法,该方法的作用是生产User实例对象。

例如在前期案例中声明数组就是使用伴生对象方式创建的。
var a=new Array[Int](3)
var b=Array(1,2,3)    //伴生对象方式

使用unapply方法能够将对象中的一些属性反解出来

object User{
  def apply(id: Int, name: String, age: Int): User = new User(id, name, age)
  def unapply(user: User): Option[(Int, String, Int)] = {
    Some(user.id,user.name,user.age)
  }
}

val u = new User(1,"zs",7)
u.name="zhangsan"
var User(age,name,birth)=u
println(s"$age\t$name\t$birth")

注意一个伴生对象中只能有一个unapply方法,这不同于apply方法,因为apply方法可以有多个(重载)。

抽象类

和Java中抽象类类似,不可以创建对象,但是可以有构造,也可以有已实现的方法(这一点和java一样)。使用abstract修饰。

abstract class Animal(name:String) {	//类上加参数,默认有构造
  def sleep(): Unit ={		//已实现的方法
    println(s"${name} 会睡觉!")
  }
  def eat():Unit
}
Trait(特征)

类似于Java中的接口,不可以创建对象,也不可以有构造。实际上它比接口还功能强大,与接口不同的是,它还可以定义属性和方法的实现。子类继承特征可以实现未被实现的方法。所以其实 Scala Trait(特征)更像 Java 的抽象类。

trait Speakable {
  def speek():Unit
  
  //有实现的方法
  def eat(a:Int,b:Int): Int ={
    println("吃饭");
    a+b;
  }
}

trait Flyable {
  def fly():Unit
}

继承&实现
//因为Animal是抽象类,携带的有参数(写在类上的构造方法),所以这里Bird继承时也要带参数
class Bird(name:String) extends Animal(name:String) with Flyable with Speakable {
  override def sleep(): String = {
    s"$name 睡觉"
  }

  override def fly(): Unit = {
    println(s"$name 会飞!")
  }

  override def speek(): Unit = {
    println(s"$name 唧唧咋咋!")
  }
}
动态植入
class SmallBird(var name:String) extends Animal (name:String) with Speakable {
  override def sleep(): String = {
    s"$name 会睡觉!"
  }

  override def speek(): Unit = {
    println(s"$name 咿咿呀呀!")
  }
  override def eat(): Unit = {
    println(s"$name 吃鱼!")
  }
}
//创建对象时,实现其他trait和trait中的方法
var sb=new SmallBird("丑小鸭") with Flyable{
  override def fly(): Unit = {
    println(s"$name 会飞!")
  }
}
sb.fly()

1.在覆盖有实现的方法必须添加overwrite;
2.一个类只能继承一个类(抽象)with多个trait例如:
class A extends B with C with D{}      //B既可以是Trait也可以是Class,但是C/D必须是Trait

枚举

需要继承Enumeration类

object EnumTest extends  Enumeration{
  val a = Value(1,"Mon");
  val b = Value(2,"Tue");
}
self

等价于this关键字,在this出现混淆的时候使用self给this关键字起别名。要求必须写在类代码块的第一行

class User(id:Int,name:String) {
  self =>//必须第一行
  var salary:Double=_
  def this(id:Int,name:String,salary:Double){
    this(id,name)
    self.salary=salary // 等价于this.salary=salary
  }
}
Case class

case class就像常规类,case class适用于对不可变数据进行建模。

case class User(name:String,age:Int)


var u1= User("zs",19)
var u2= User("zs",19)
//u1.name="lisi";//会报错,name和age默认是用val修饰的,除非在case class的声明上改为var
println(u1==u2) //true

var u1= new User("zs",19)
var u2= new User("zs",19)
println(u1==u2) //true

与普通的class不同的是,CaseClass创建的对象 == 比较的是对象的内容。通常用于只读数据的建模。可以简单的使用copy来实现两个对象间的值得传递

var u1= User("zs",19)
var u2= u1.copy(u1.name,20)
println(u2)  //zs,20
final用法
  • 修饰类 ,表示最终类,该类无法被继承
  • final修饰方法,表示方法不允许被覆盖
  • final修饰属性,表示改属性不允许被子类遮盖,而在Java则表示常量。因为Scala使用val关键字表示属性能否被修改。

java中abstract不可以和final连用,但Scala中可以(不会报错)

可见性

Public(scala中不存在public)
  • Scala 中的默认可见性为 public,所谓默认即你没有在类或者成员前显示加 private 或 protected 可见性关键字。虽然默认可见性为 public,但这是逻辑上的,实际上 Scala 中并没有 public 这个关键字,如果你用 public 来声明一个类或成员,编译器会报错。

    在 Scala 中,可以在类型的 class 或 trait 关键字之前、字段的 val 或 var 之前,方法定义的 def 关键字之前指定可见性。如果不指定默认都是public。
Private
  • 修饰类,同包可见,不同包不可见,可被同包子类继承(子类也需要使用private修饰)

该类只能被同包下的子类继承,不同包类不可见,并且同包继承的类也必须是private修饰。因为这样才不会改变原始父类的可见性。被private修饰的类只能在同包下可用。

  • 修饰属性/方法

在修饰属性表示该属性/方法只能被本类|伴生对象可见,其他均不可见。

Protected
  • 修饰类

修饰类 该类可以被同包下的其他类访问。或者是同包子类使用。

  • 修饰属性和方法

被Protected修饰属性和方法只能在本类或者本类的子类以及半生对象中访问。

this限定

可以去除伴生对象的可见性

class Student(name:String) {
   var id=1
   protected|private[this] var sex=true	//该属性伴生对象不可见,只能在本类中或者子类中使用
   def sayHello():Unit={
     println(this.name)
   }
}
object Student{
  def apply(name: String): Student = new Student(name)
}
package限定

表示当前的类、属性、方法可以在指定的包下可见。

class Student(name:String) {
   var id=1
   private [demo01] var sex=true	//demo01:包名,只能在demo01中访问sex属性,其他包下不可以
   def sayHello():Unit={
     println(this.name)
   }
}
object Student{
  def apply(name: String): Student = new Student(name)
}
sealed(密封)

Trait和class、case class可以标记为 sealed,这意味着必须在同一文件中声明所有子类型这样就确保了所有的子类型都是已知的。

//下面的都声明在了一个class中
sealed case class Message(msg:String,code:Int)
class InfoMessage(msg:String,code:Int) extends Message(msg:String,code:Int) 
class ErrorMessage(msg:String,code:Int) extends Message(msg:String,code:Int) 
class WarnMessage(msg:String,code:Int) extends Message(msg:String,code:Int) 

函数对象

在Java1.8中提出了一个Functional Interface,如果一个接口里面只能有一个抽象方法。这种类型的接口也称为SAM接口,即Single Abstract Method interfaces。将该接口成为函数式接口。

//java中可以通过FunctionalInterface注解标明此接口为函数式接口
@FunctionalInterface
public interface GreetingService {
    void sayMessage(String message);
}
//java中Lambda简写实现
GreetingService  gs= (message -> System.out.println("hello,"+message) );

//不用Lambda简写
GreetingService  gs= new GreetingService  () {
            @Override
            public void saying(String message) {
                System.out.println("hello,"+ message);
            }
        };
gs.sayMessage("张三");

Java 8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。更多参考:https://www.cnblogs.com/chenpi/p/5890144.html

部分应用函数

在Scala中同样对所有的函数都可以理解为是一个接口函数。例如在Scala中可以将任意一个函数转变成对象。例如如下定义一个sum函数。

scala> def sum(v1:Int,v2:Int):Int={
     |       v1+v2
     |    }
sum: (v1: Int, v2: Int)Int

在Scala中可以尝试将一个函数转变成一个对象类型,如下:

scala> var sumFunction=sum _
sumFuction: (Int, Int) => Int = <function2>

这里的sumFunction就是sum函数的部分应用函数。例如只修改其中一个参数的值:

scala> var sumForOne=sum(_:Int,2)
sumForOne: Int => Int = <function1>

可以看出sumFunction是一个变量,该变量的类型是(Int, Int) => Int其中<function2>是该对象的字面值。在Scala中左右的函数都可以更改成为Function1~22对象的实例。例如

var f1:Function2[Int,Int,Int]= (x,y)=>x +y
var f2:(Int,Int)=>Int = (x,y)=>x +y
f1.apply(1,2)
f1(1,2)
f2.apply(1,2)
f2(1,2)

通过以上的论证我们可以将

Function2[T1,T2,R] === (T1,T2)=>R

也就意味着(T1,T2)=>R表示Function2类型。在Scala中有一种更简洁的方法去描述Function1~22的简便写法,例如改写上述sum函数

scala> var sumFunction:(Int,Int)=>Int = (x:Int,y:Int)=> x+y
sumFunction: (Int, Int) => Int = <function2>

scala> sumFunction(1,2)
res34: Int = 3

scala> var f1:Function2[Int,Int,Int]= (x,y)=>x +y
f1: (Int, Int) => Int = <function2>

PartitalFunction

偏函数主要适用于处理指定类型的参数数据,通常用于集合处理中。定义一个函数,而让它只接受和处理其参数定义域范围内的子集,对于这个参数范围外的参数则抛出异常,这样的函数就是偏函数(顾名思异就是这个函数只处理传入来的部分参数)。偏函数是个特质其类型为PartialFunction[A,B],其中接收一个类型为A的参数,返回一个类型为B的结果

class MyPartialFunction extends PartialFunction[Any, Int]{
  override def isDefinedAt(x: Any): Boolean = {
    x.isInstanceOf[Int]
  }

  override def apply(v1: Any): Int = {
    if (v1.isInstanceOf[String]){
      v1.asInstanceOf[String].toInt *3
    }else{
      0
    }
  }
}

object MyPartialFunction{
  def main(args: Array[String]): Unit = {
    var pf=new MyPartialFunction
    println(pf.apply("222"))  //输出666
    
    //这里直接声明一个偏函数
    var pf2:PartialFunction[Any,Int]={
      case x:Int => x*2
    }
   println( pf2("111"))	//会报错,因为pf2代码中代码中没有输入值String且返回值为int的代码

  }
}

例如在Scala的数组中就有对偏函数的使用:

var a=Array(1,2,3,"a","b")
a.collect({case x:Int => x*2})
for(item <- a;if(item.isInstanceOf[Int])) yield item.asInstanceOf[Int]*2

异常处理

Scala 的异常处理和其它语言比如 Java 类似。Scala 的方法可以通过抛出异常的方法的方式来终止相关代码的运行,不必通过返回值。Scala 抛出异常的方法和 Java一样,使用 throw 方法,例如,抛出一个新的参数异常:

throw new ArithmeticException()

如果有异常发生,Scala中try catch子句是按次序捕捉的。因此,在catch字句中,越具体的异常越要靠前,越普遍的异常越靠后。 如果抛出的异常不在catch子句中,该异常则无法处理,会被升级到调用者处。捕捉异常的catch子句,语法与其他语言中不太一样。在Scala里,借用了模式匹配的思想来做异常的匹配,因此,在catch的代码里,是一系列case子句。

try {
    //var i: Int = 10 / 0
    throw new java.io.IOException()
} catch {
    case  e: ArithmeticException => {
        println("e1:"+e.getMessage)
    }
    case  e: Exception => {
        println("e2:"+e.getMessage)
    }
}

注意在Scala中并不会检查捕获异常的顺序(即使大范围异常写在小范围异常前,也不会报错),Scala并不区分运行异常和检查异常。例如:

def main(args: Array[String]): Unit = {
  throw new java.io.IOException()
}

finally 语句和java一样,用于执行不管是正常处理还是有异常发生时都需要执行的步骤,实例如下:

try {
    println("--------")
    var i=10/0
    println("++++++++")
} catch {
    case e:Exception =>{
        println("e:"+e.getMessage)
    }
} finally {
    println("finally")
}

Scala导包(一般使用idea会自动导包,不需要手动导)

  • 导入具体的一个包

import java.util.Date
`- 导入特定的一个包下的几个类
import java.util.{Date, HashMap}

  • 导入一个包下所有的类

import java.util._

  • 当一个类中存在命名冲突,可以起别名
import scala.collection.mutable.HashMap
import java.util.{HashMap=>JHashMap}

val jmap = new JHashMap[String,Int]()
//也可以像java一样写类的全路径,个人更喜欢这一种,毕竟不用手写
val hmap = new  java.util.HashMap[String,Int]();

jmap.put("aa",1)
val map = HashMap(("aa",1))
/*map.get("aa")获取到的不是1,如果"aa"存在,则获取的是是Some(1)对象,不存在获取的是None
如果想要获取到1,需要在后面再调用个get,即:map.get("aa").get,
但因为不知道"aa"是否存在,所以链式调用后一个get时可能空指针,所以为了避免空指针,
还可以调用getOrElse函数,存在"aa"则返回值,不存在则返回getOrElse后跟的值
*/
println(map.get("aa").getOrElse(0))	//存在aa返回值,不存在返回0,因为getOrElse括号中写的是0
println(hm.getOrElse("aa",0))	//等价于上面
println(jmap.get("aa"))

隐式传值\隐式转换

隐式值获取
implicit val i:Int=2 //用作隐式值注入
implicit val s:String="Hello" //用作隐式值注入

val b=implicitly[Int]     //当前类文件中,只能有一个Int隐式值类型
val ss=implicitly[String] //当前类文件中,只能有一个String隐式值类型
println("b:"+b+"\tss:"+ss)

使用implicitly[类型],必须保证当前上下文有且仅有一个隐式值类型,一般这种隐式值变量的声明写在object单例类或者伴生对象中。例如:

object MyImplicits {
  implicit val i:Int=2 //用作隐式值注入
  implicit val s:String="Hello" //用作隐式值注入
}
import MyImplicits._	//一定要导包,否则不知道隐式传值从哪获取
val b=implicitly[Int]     //获取的就是MyImplicits中的隐式Int值
val ss=implicitly[String] //获取的就是MyImplicits中的隐式String值
println("b:"+b+"\tss:"+ss)
隐式注入值

implicit放置在参数上时候,只能放在第一参数上。

def sayHello(implicit msg:String,name:String):Unit={
    println(msg+" "+name)
}
import MyImplicits._
sayHello(implicitly[String],"zhansgan") //隐式值注入,只能放在第一个参数

由此可以看出,这种写法不够简洁,Scala推荐大家如果使用隐式值注入,一般使用柯里化风格

def sayHello(name:String)(implicit msg:String):Unit={
    println(msg+" "+name)
}
import MyImplicits._
sayHello("zhansgan")//这里用的柯里化,会自动将MyImplicits中的值注入

要求需要自动值注入类型,必须选在最后面

隐式参数转换

该方式是通过隐式转换将参数不满足的类型转为所需类型,Scala在编译代码的时候,先尝试正常编译如果发现编译类型不匹配,会尝试加载当前上下文中是否存在该类型和目标类型的一种隐式转换{这种转换严格意义上应该必须唯一(只有一个相同参数的隐式转换),否则编译器会提示错误警告,并不影响执行},如果存在则编译通过。

object MyImplicits {
  //定义一个隐式转换,将String转为Data
  implicit def str2Date(s:String):Date={
    val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    sdf.parse(s)
  }
}
import MyImplicits._

def tellTime(date:Date):Unit={
    println(date.toLocaleString)
}
tellTime(new Date())
tellTime("2019-06-26 10:43:00")//通过隐式参数转换,将String转为Date,然后作为方法的参数
隐式方法增强

该方式是通过隐式转换将类的方法进行增强处理,当对目标对象调用不存在的方式时候,Scala会尝试加载当前上下文中是否存在该类的隐式增强类(这种转换严格意义上应该必须唯一,否则编译器会提示错误警告,并不影响执行),如果存在则编译通过。

object MyImplicits {

  implicit class PigImplicits(pig:Pig){
    def fly():Unit={
      println(pig.name+" 会飞!")
    }
  }
}
class Pig(var name:String) {
  def eat():Unit={
    println(s"$name eat")
  }
  def sleep():Unit={
    println(s"$name sleep")
  }
}
import MyImplicits._

val pig = new Pig("佩奇")
pig.eat()	//pig调用eat和sleep都没问题,因为这些都是Pig类中的方法
pig.sleep()
pig.fly()	//Pig中没有fly方法,但有隐式注入MyImplicits,它里面定义的有fly,这里就可以调用

Java集合转换为Scala

import java.util.ArrayList
import scala.collection.JavaConverters._
val array = new ArrayList[Int]()
array.add(1)
array.add(2)
array.add(3)
array.add(4)

//使用Java API实现累加
val sum = array.stream().reduce(new BinaryOperator[Int] {
    override def apply(t: Int, u: Int): Int = {
        t + u
    }
}).get()
//通常引入JavaConverters之后,可以使用加强版本的集合操作
var total=array.asScala.sum

Scala 泛型

<: 设置上边界
class Keeper[U <: Dog] {
  def keep(u:U):Unit={
    println(u)
  }
}

表示只可以传递Dog或者Dog的子类

>:设置下边界
class Keeper[U >: Dog] {
  def keep(u:U):Unit={
    println(u)
  }
}

表示设置下边界, 泛型只允许设置Dog或者是Dog的父类

<%视图限定

视图限定将会在后续版本移除,主要意思是指必须保证上下文中有能够提供一个隐式转换T <% U能够将T隐式转为U类型,如下所示要求上下文中能够尝试将一个String类型隐式转换为Date类型。

object MyPredefs{
  //定义一个隐式转换,将String转为Data
  implicit def str2Date(s:String):Date={
    val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    sdf.parse(s)
  }
}
def sayTime[String <% Date](date:String):Unit={
    println(date.getTime)
}

import MyPredefs._
sayTime("2019-06-26 14:51:00")
T:A上下文绑定

表示上下文中必须存在这种隐式值A[T]隐式值,否则程序编译出错.这样可以在上下文中还没有隐式值得时候确保方法能编译成功。

implicit val u1=new User[String]()
def sayInfomation[T:User](msg:T):Unit={
  var u=implicitly[User[T]]
   u.sayHello(msg)
}

要求上下文中必须有一个隐式值User[T]类型,例如

implicit val u1=new User[String]()	//User隐式值
def sayInfomation[T](msg:T):Unit={
  var u=implicitly[User[T]] //如果没有User隐式值,那就无法获取上下文的隐式值
   u.sayHello(msg)
}
class User[T] {
    def sayHello(t:T): Unit ={
      println(t)
    }
}
协变、逆变、不变(参照java的多态理解)
+A协变
class Covariant[+T](t:T) {
}

//其中,Animal是Dog的父类,dog又是SmallDog的父类
val c1 = new Covariant[Dog](new Dog("小狗"))
val c2 = new Covariant[SmallDog](new SmallDog("小狗",true))
var c3:Covariant[SmallDog]= c1	//error
var c4:Covariant[Animal]= c2	//true

个人理解:协变是可以将子类型赋值给父类型(小的赋值给大的)

-A逆变
class Covariant[T](t:T) {
    
}

val c1 = new Covariant[Dog](new Dog("小狗"))
val c2 = new Covariant[SmallDog](new SmallDog("小狗",true))
var c3:Covariant[SmallDog]= c1	//true
var c4:Covariant[Dog]= c2	//error

个人理解:逆变是可以将父类型赋值给子类型(大的赋值给小的)

A不变
class Covariant[T](t:T) {

}


val c1 = new Covariant[Dog](new Dog("小狗"))
val c2 = new Covariant[SmallDog](new SmallDog("小狗",true))
var c3:Covariant[SmallDog]= c1//error
var c4:Covariant[Dog]= c2     //error
var c5:Covariant[Dog]= c1
var c6:Covariant[SmallDog]= c2

同类型赋值给同类型,java中就是不变

集合/数组(重点)

Array在Scala中指的是数组,创建方式如下所示,可以看出分配大小为10,每个元素都分配了默认值。

scala> var array=new Array[Int](10)
array: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
  • 修改指定位置元素
//1.直接赋值
scala> array(0)=1
res57: Array[Int] = Array(1, 0, 0, 0, 0, 0, 0, 0, 0, 0)

//2.调用updat(下标,值)
scala> array.update(1,2)
res62: Array[Int] = Array(1, 2, 0, 0, 0, 0, 0, 0, 0, 0)
  • 获取数组长度
array.length
array.size
  • 数组去重
scala> array
res66: Array[Int] = Array(1, 2, 0, 0, 0, 0, 0, 0, 0, 0)

scala> array.distinct
res67: Array[Int] = Array(1, 2, 0)
Range

该元素指定在创建的时候指定步长,相比较于Array,该Range只读,不允许修改。

Range(0,10,2)
//0:从哪开始;10:到哪结束;2:步长
scala>  var range= new Range(0,10,2)
range: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8)

//0 to 10 by 3:和上面一样,10能取到
scala> var range= 0 to 10 by 2
range: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8, 10)

scala> var range= 0 to 10 by 3
range: scala.collection.immutable.Range = Range(0, 3, 6, 9)

//0 until 10 by 2:和上面一样,10取不到
scala> var range=0 until 10 by 2
range: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8)
Vector

按照下标顺序排列的向量(可以理解为数学中的向量 X轴、Y轴、Z轴......),向量不支持修改。

scala> var vec=Vector(0,10,1)
vec: scala.collection.immutable.Vector[Int] = Vector(0, 10, 1)

scala> var vector=for(i<- 0  to 10 by 2) yield i
vector: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 2, 4, 6, 8, 10)
Iterator

遍历后,Iterator为空(只能遍历一次)

scala> var it=Iterator(1,2,3)
it: Iterator[Int] = non-empty iterator

scala>it.foreach(t=>println(t))
//scala> it.foreach(println)	//简写,作用和上一行一样,都是遍历
1
2
3

scala> it.isEmpty
res41: Boolean = true
List(不可修改元素值)
:+ 追加元素(追加的元素放在末尾)
scala> var list=List(1,2,3)
list: List[Int] = List(1, 2, 3)

scala> list :+ -10
res0: List[Int] = List(1, 2, 3, -10)
::或者+: 追加元素(追加的元素放在第一个位置)
scala> var list=List(1,2,3)
list: List[Int] = List(1, 2, 3)

scala> list::=10

scala> list
res84: List[Int] = List(10, 1, 2, 3)

scala> list(0)=11
<console>:13: error: value update is not a member of List[Int]
       list(0)=11
       ^

scala> list +:= -1

scala> list
res87: List[Int] = List(-1, 10, 1, 2, 3)
:::合并集合

a:::=(b),在a集合元素之前追加b集合元素,生成新的集合
注意:有=是向前加,没=号是向后添加

scala> var list=List(1,2,3)
scala> var prefix=List(4,5,6)
scala> list :::= (prefix) //在list元素之前追加prefix元素,生成新的集合
scala> list
res32: List[Int] = List(4, 5, 6, 1, 2, 3)

小结:
可能通过CMD控制台看scala不清晰,而且很抽象,下面在scala中在写一遍

var list=List(1,2,3)
//向后追加数据
val list2: List[Int] = list.:+(4)	//向后追加元素 
val list3: List[Int] = list :+ 5	//向后追加元素 和前一行相比去掉.和() 
list.:+=(6);						//向后追加元素,等同于list = list.:+(5)  返回值为Unit

println(list2)		//结果 1,2,3,4
println(list3)		//结果 1,2,3,5
println(list)		//结果 1,2,3,6

//向前追加元素
//1.把追加元素写在后面  使用.+:()   此时.和()都不能省略,否则编译报错
val list4: List[Int] = list.+:(7)	 //等于 list.::(7)
list.+:= (8)		//等于 list.::=(8)  等同于list = list.+:(5)
//2.把追加元素写在前面, .和()都必须省略,否则编译不通过
val list5: List[Int] = 9 +: list   //等于 9 :: list  
4.+:(list)		//编译不通过, .必须省略
4 +:=(list)		//编译不通过 把追加元素放在前面时,不能有等号没有 相当于没有 i = 1+i 写法

println(list4)		//结果 7,1,2,3
println(list5)		//结果 8,2,3,5
println(list)		//结果 9,8,1,2,3

总结::+和+: 两者的区别在于:+方法用于在尾部追加元素,+:方法用于在头部追加元素,和::很类似,但是::可以用于pattern match ,而+:则不行. 关于+:和:+,只要记住冒号永远靠近集合类型就OK了,加号位置决定元素(无论元素还是集合)加在前还是后。 带=就等同于 i+=1

drop/dropRight

删除前n个元素

scala> var list=List(1,2,3)
list: List[Int] = List(1, 2, 3)
scala> list.drop(2) //删除前n个元素
res30: List[Int] = List(3)
slice

截取(左闭右开),类似于String的subString方法

scala> var list=List(1,2,3)
list: List[Int] = List(1, 2, 3)

scala> list.slice(1,3)
res104: List[Int] = List(2, 3)

slice(a,b):b是可以大于集合长度的 ,不会报错
reverse

前后顺序颠倒

scala> var list=List(1,2,3)
list: List[Int] = List(1, 2, 3)

scala> list.reverse
res105: List[Int] = List(3, 2, 1)
take/takeRight

take(n):从集合第一个元素拿到第n个
takeRight(n):和take作用一样,不过是从后面拿

scala> var list=List(1,2,3,4,5,6)
list: List[Int] = List(1, 2, 3,4,5,6)

scala> list.take(2)
res106: List[Int] = List(1, 2)

scala> list.takeRight(2)
res107: List[Int] = List(5, 6)
takeWhile
scala> var list=List(1,2,3)
list: List[Int] = List(1, 2, 3)

scala> list.takeWhile(x=> x<3 )
res108: List[Int] = List(1, 2)

scala> list.takeWhile(x=> x%2==1 )
res110: List[Int] = List(1)
head/last/tail
scala> var list=List(1,2,3,4,5,6)
list: List[Int] = List(1, 2, 3,4,5,6)

scala> list.head	//获取第一个元素
res111: Int = 1

scala> list.tail	//获取除了第一个元素的其他元素
res112: List[Int] = List(2, 3)

scala> list.last	//获取最后一个元素
res113: Int = 3
ListBuffer
update
scala> import scala.collection.mutable._
import scala.collection.mutable._

scala> var list=ListBuffer[Int]()
list: scala.collection.mutable.ListBuffer[Int] = ListBuffer()

scala> var list=ListBuffer(1,2,3)
list: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3)

scala> list.update(0,10)

scala> list
res1: scala.collection.mutable.ListBuffer[Int] = ListBuffer(10, 2, 3)
+=或者+=:或者++=或者++=:

添加元素

scala>  var list=ListBuffer(1,2,3)
list: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3)

scala> list.+=(4)
res12: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4)

scala> list.+=:(5)
res13: scala.collection.mutable.ListBuffer[Int] = ListBuffer(5, 1, 2, 3, 4)

scala> list.++=(List(6,7,8))
res14: scala.collection.mutable.ListBuffer[Int] = ListBuffer(5, 1, 2, 3, 4, 6, 7, 8)

+=集合末尾添加元素,+=:集合开始添加元素,++=追加集合在尾部,++=:在元素尾部添加元素
像集合中添加元素时,因为+=、+=:很容易记混记错,所以最好用append和prepend

-/-=
scala> list.-(1)
res18: scala.collection.mutable.ListBuffer[Int] = ListBuffer(2, 3, 1)

scala> list
res19: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 1)

scala> var list=ListBuffer(1,2,3,1)
list: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 1)

scala> list.-=(1)
res20: scala.collection.mutable.ListBuffer[Int] = ListBuffer(2, 3, 1)

scala> list
res21: scala.collection.mutable.ListBuffer[Int] = ListBuffer(2, 3, 1)

区别-表示创建新的集合但是不包含删除的这个元素,-=表示删除集合指定元素,更新原始集合

remove

删除指定位置元素

scala> var list=ListBuffer(1,2,3)
list: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3)

scala> list.remove(0)
res25: Int = 1

scala> list
res26: scala.collection.mutable.ListBuffer[Int] = ListBuffer(2, 3)
insert/insertAll

在指定位置之前插入元素

scala> list
res27: scala.collection.mutable.ListBuffer[Int] = ListBuffer(2, 3)

scala> list.insert(0,4)

scala> list
res29: scala.collection.mutable.ListBuffer[Int] = ListBuffer(4, 2, 3)

scala> list.insertAll(2,List(1,2,3))

scala> list
res31: scala.collection.mutable.ListBuffer[Int] = ListBuffer(4, 2, 1, 2, 3, 3)
prependToList

将list1结果添加到list2中

scala>  var list1=List(1,2,3)
list1: List[Int] = List(1, 2, 3)

scala>  var list2=ListBuffer(0,0)
list2: scala.collection.mutable.ListBuffer[Int] = ListBuffer(0, 0)

scala> list2.prependToList(list1)
res34: List[Int] = List(0, 0, 1, 2, 3)

scala> list2
res36: scala.collection.mutable.ListBuffer[Int] = ListBuffer(0, 0, 1, 2, 3)
Set(不可修改)
+或者+=和-或者-=
scala> var s=Set[Int](1,2,4,6,2)
s: scala.collection.immutable.Set[Int] = Set(1, 2, 4, 6)

scala> s.+=(1)

scala> s
s: scala.collection.immutable.Set[Int] = Set(1, 2, 4, 6)

scala> s+(7)
res12: scala.collection.immutable.Set[Int] = Set(1, 6, 2, 7, 4)

区别是+创建新的Set并添加元素,不修改原始set,+=修改原始set,类似-、-=就不在阐述

size

获取长度

scala> var s=Set[Int](1,2,4,6,2)
s: scala.collection.immutable.Set[Int] = Set(1, 2, 4, 6)

scala> s.size
res23: Int = 4
Set(可修改)
+/+=/-/-=
scala> var s=scala.collection.mutable.Set(1,2,3)
s: scala.collection.mutable.Set[Int] = Set(1, 2, 3)

scala> s.+(4)
res25: scala.collection.mutable.Set[Int] = Set(1, 2, 3, 4)

scala> s
res26: scala.collection.mutable.Set[Int] = Set(1, 2, 3)

scala> s.+=(4)
res27: scala.collection.mutable.Set[Int] = Set(1, 2, 3, 4)

scala> s
res28: scala.collection.mutable.Set[Int] = Set(1, 2, 3, 4)

scala> s.-(2)
res29: scala.collection.mutable.Set[Int] = Set(1, 3, 4)

scala> s
res30: scala.collection.mutable.Set[Int] = Set(1, 2, 3, 4)

scala> s.-=(2)
res31: scala.collection.mutable.Set[Int] = Set(1, 3, 4)

scala> s
res32: scala.collection.mutable.Set[Int] = Set(1, 3, 4)

remove/add
scala> var s=scala.collection.mutable.Set(1,2,3)
s: scala.collection.mutable.Set[Int] = Set(1, 2, 3)

scala>  s.remove(2)
res37: Boolean = true

scala>  s.remove(-1)
res38: Boolean = false

scala> s
res39: scala.collection.mutable.Set[Int] = Set(1, 3)

scala> s.add(4)
res41: Boolean = true
HashMap(不可变)

不可直接对原位置的元素进行更改 会返回一个新的值(需定义 VAL 值名)

+/+=/-/-=
scala> import scala.collection.immutable.HashMap
import scala.collection.immutable.HashMap

scala> var hm=HashMap[String,String](("建设","001"),("招商","002"))
hm: scala.collection.immutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002)

scala> hm+="工商"->"003"

scala> hm.+=(("工商1","004"))

scala> hm
res4: scala.collection.immutable.HashMap[String,String] = Map(工商 -> 003, 建设 -> 001, 招商 -> 002, 工商1 -> 004)

scala> hm.-("工商")
res6: scala.collection.immutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002, 工商1 -> 004)

scala> hm
res7: scala.collection.immutable.HashMap[String,String] = Map(工商 -> 003, 建设 -> 001, 招商 -> 002, 工商1 -> 004)

scala> hm.-=("工商")

scala> hm
res9: scala.collection.immutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002, 工商1 -> 004)

get/getOrElse
scala> import scala.collection.immutable.HashMap
import scala.collection.immutable.HashMap

scala> var hm=HashMap[String,String](("建设","001"),("招商","002"))
hm: scala.collection.immutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002)

scala> hm.get("招商").getOrElse("没有值")
//等价于:hm.getOrElse("招商,"没有值")
res11: String = 002

scala> hm.get("招商1").getOrElse("没有值")
res12: String = 没有值

size
scala> hm
res16: scala.collection.immutable.HashMap[String,String] = Map(工商 -> 003, 建设 -> 001, 招商 -> 002, 工商1 -> 004)

scala> for(i<- hm) println(i._1+" -> "+i._2)
工商 -> 003
建设 -> 001
招商 -> 002
工商1 -> 004

merged
scala> var hm=HashMap[String,String](("建设","001"),("招商","002"))
hm: scala.collection.immutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002)

scala> var newhm=HashMap[String,String](("邮政","004"))
newhm: scala.collection.immutable.HashMap[String,String] = Map(邮政 ->
                                                               
                                                                                                                          
scala> var mergerFunction:((String,String),(String,String))=>(String,String) = (tuple1,tuple2)=>{
     |
     |    if(tuple1._1.equals(tuple2._1)){
     |       (tuple1._1,tuple2._2)
     |    }else{
     |      tuple1
     |    }
     |  }
scala> var res=hm.merged(newhm)(mergerFunction)
res: scala.collection.immutable.HashMap[String,String] = Map(邮政 -> 004, 建设 -> 001, 招商 -> 002)

scala> res
res18: scala.collection.immutable.HashMap[String,String] = Map(邮政 -> 004, 建设 -> 001, 招商 -> 002)

scala> for(i<- res) println(i)
(邮政,004)
(建设,001)
(招商,002)

keys/values
scala> var hm=HashMap[String,String](("建设","001"),("招商","002"))
hm: scala.collection.immutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002)

scala> hm.keys
res23: Iterable[String] = Set(建设, 招商)

scala> hm.values
res24: Iterable[String] = MapLike(001, 002)
HashMap(可变)

直接可以更改原位置的元素不用返回新的值

remove/put
scala> import scala.collection.mutable.HashMap
import scala.collection.mutable.HashMap

scala> var hm=HashMap[String,String](("建设","001"),("招商","002"))
hm: scala.collection.mutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002
                                                          
scala> hm+="工商"->"003"
res0: scala.collection.mutable.HashMap[String,String] = Map(工商 -> 003, 建设 -> 001, 招商 -> 002)

scala> hm-=("工商")
res1: scala.collection.mutable.HashMap[String,String] = Map(建设 -> 001, 招商 -> 002)

scala> hm.put("邮政","004")
res2: Option[String] = None
 
scala> hm.remove("工商")
res3: Option[String] = None
 
scala> for(i<- hm) println(i)
(建设,001)
(邮政,004)
(招商,002)

集合算子(重点)

Sort:排序
  • sorted
var array=Array(1,2,4,5,3)
list.sorted
//结果:List[Int] = List(1, 2, 3, 4, 5)
  • sortBy
 var list=Array(("a",1),("b",2),("d",5),("c",4))

 list.sortBy(t=>t._1) //list.sortBy(_._1)
//结果:Array[(String, Int)] = Array((a,1), (b,2), (c,4), (d,5))
  • sortWith:用户可以指定多个Field做联合排序
var list=Array(("a",2),("a",1),("b",2),("d",5),("c",4))

val tuples = list.sortWith((t1, t2) => {
            if (!t1._1.equals(t2._1)) {
              t1._1.compareTo(t2._1) < 0
            } else {
              t1._2.compareTo(t2._2) < 0
            }
          })
//结果:Array[(String, Int)] = Array((a,1), (a,2), (b,2), (c,4), (d,5))
flatten:拉平
var list=Array(Array(1,2,3),Array(4,5))
list.flatten
//结果:Array[Int] = Array(1, 2, 3, 4, 5)
map:转换
var list=Array("this is a demo","hello word")
list.map(line=>line.split(" ")) // 等价list.map(_.split(" "))
//结果:Array[Array[String]] = Array(Array(this, is, a, demo), Array(hello, word))

小综合:

list.map(_.split(" ")).flatten.map((_,1))
//结果:Array[(String, Int)] = Array((this,1), (is,1), (a,1), (demo,1), (hello,1), (word,1))
flatMap

flatMap = map + flatten 即先对集合中的每个元素进行map,再对map后的每个元素(map后的每个元素必须还是集合)中的每个元素进行flatten

var list=Array("this is a demo","hello word")
list.flatMap(_.split(" "))
//结果:Array[String] = Array(this, is, a, demo, hello, word)

list.flatMap(_.split(" ")).map((_,1))
//结果:Array[(String, Int)] = Array((this,1), (is,1), (a,1), (demo,1), (hello,1), (word,1))

//或者
list.flatMap(line=> (for(i<-line.split(" ")) yield (i,1)))
//结果和上面一样:Array[(String, Int)] = Array((this,1), (is,1), (a,1), (demo,1), (hello,1), (word,1))
filter/filterNot
var list=Array("this is a demo","hello word")
list.filter(line=>line.contains("hello"))
//结果:Array[String] = Array(hello word)

list.filterNot(line=>line.contains("hello"))
//结果:Array[String] = Array(this is a demo)
groupBy
var list=Array("good","good","study")
list.groupBy(item=>item)
//结果:Map(study -> Array(study), good -> Array(good, good))

list.groupBy(item=>item).map(t=>(t._1,t._2.size))
//结果:Map(study -> 1, good -> 2)
fold(聚合算子)
var lst=Array(1, 2, 3, 1, 2, 4, 5, 6, 7, 9, 4)
 lst.fold(-1)((x,y)=>{println("seqop",x,y,x+y);x+y}) // lst.fold(0)(_+_)
 (seqop,-1,1,0)
(seqop,0,2,2)
(seqop,2,3,5)
(seqop,5,1,6)
(seqop,6,2,8)
(seqop,8,4,12)
(seqop,12,5,17)
(seqop,17,6,23)
(seqop,23,7,30)
(seqop,30,9,39)
(seqop,39,4,43)
res55: Int = 43
//0:初始值,和aggregate作用一样,加法不一样:0+v1后与v2加,其结构再和后续的v...依次相加
//初始值+元素1+元素2+元素3+...最后一个元素
结果:41
aggregate(聚合算子)

会把数组/集合分为若干份,先将每个分组后的内部运算(v1,v2),然后将每个运算结果再整体运算(b1,b2),但此函数若要分区计算,需要后跟par转换为并行集合

list.aggregate(初始值)(分区内计算 ,分区间计算 )
list.aggregate(初始值)(初始值 +分区元素值, 某一分区值 + 另一分区值)

val a = List(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24)

不分区计算:

//没有分区,相当于直接从初始值加到末尾,分区间计算没用到
//等同于a.fold(0)((v1,v2)=>v1+v2)		初始值+元素1+元素2+元素3+...最后一个元素
scala> a.aggregate(0)((x,y)=>{println("seqop",x,y,x+y);x+y},(x,y)=>{println(x,y,x+y);x+y})
(seqop,0,1,1)
(seqop,1,2,3)
(seqop,3,3,6)
(seqop,6,4,10)
(seqop,10,5,15)
(seqop,15,6,21)
(seqop,21,7,28)
(seqop,28,8,36)
(seqop,36,9,45)
(seqop,45,10,55)
(seqop,55,11,66)
(seqop,66,12,78)
(seqop,78,13,91)
(seqop,91,14,105)
(seqop,105,15,120)
(seqop,120,16,136)
(seqop,136,17,153)
(seqop,153,18,171)
(seqop,171,19,190)
(seqop,190,20,210)
(seqop,210,21,231)
(seqop,231,22,253)
(seqop,253,23,276)
(seqop,276,24,300)
res96: Int = 300

分区计算
注意:这里很坑

scala> a.par.aggregate(-1)((x,y)=>{println("seqop",x,y,x+y);x+y},(x,y)=>{println(x,y,x+y);x+y})
(seqop,-1,7,6)
(seqop,-1,9,8)
(seqop,-1,8,7)
(seqop,-1,13,12)
(seqop,-1,19,18)
(seqop,-1,20,19)
(seqop,-1,11,10)
(seqop,-1,12,11)
(seqop,-1,3,2)
(seqop,-1,2,1)
(seqop,-1,6,5)
(seqop,-1,5,4)
(seqop,-1,4,3)
(seqop,-1,1,0)
(seqop,-1,10,9)
(4,5,9)
(seqop,-1,22,21)
(1,2,3)
(seqop,-1,16,15)
(10,11,21)
(seqop,19,21,40)
(seqop,-1,14,13)
(7,8,15)
(seqop,13,15,28)
(18,40,58)
(9,21,30)
(0,3,3)
(seqop,-1,18,17)
(seqop,-1,23,22)
(seqop,-1,17,16)
(3,9,12)
(16,17,33)
(seqop,22,24,46)
(12,28,40)
(6,15,21)
(21,46,67)
(15,33,48)
(3,12,15)
(40,48,88)
(58,67,125)
(21,30,51)
(88,125,213)
(15,51,66)
(66,213,279)
res17: Int = 279
//多运行两次,还可能是下面结果
res16: Int = 280
res13: Int = 281
res18: Int = 277

可能通过数字的累加看清楚,下面用字符串展示一下:

val b = List("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q")
b.par.aggregate("&")((x,y)=>{println("seqop",x,y,x+y);x+y},(x,y)=>{println(x,y,x+y);x+y})
(seqop,&,o,&o)
(seqop,&,e,&e)
(seqop,&,j,&j)
(seqop,&,a,&a)
(seqop,&,c,&c)
(seqop,&,m,&m)
(seqop,&,g,&g)
(seqop,&,i,&i)
(seqop,&,b,&b)
(seqop,&,k,&k)
(seqop,&,d,&d)
(&a,&b,&a&b)
(&i,&j,&i&j)
(seqop,&,n,&n)
(seqop,&,h,&h)
(seqop,&,l,&l)
(seqop,&,f,&f)
(seqop,&,p,&p)
(&e,&f,&e&f)
(&k,&l,&k&l)
(&g,&h,&g&h)
(&m,&n,&m&n)
(&c,&d,&c&d)
(&e&f,&g&h,&e&f&g&h)
(&i&j,&k&l,&i&j&k&l)
(seqop,&p,q,&pq)
(&a&b,&c&d,&a&b&c&d)
(&o,&pq,&o&pq)
(&a&b&c&d,&e&f&g&h,&a&b&c&d&e&f&g&h)
(&m&n,&o&pq,&m&n&o&pq)
(&i&j&k&l,&m&n&o&pq,&i&j&k&l&m&n&o&pq)
(&a&b&c&d&e&f&g&h,&i&j&k&l&m&n&o&pq,&a&b&c&d&e&f&g&h&i&j&k&l&m&n&o&pq)
res54: String = &a&b&c&d&e&f&g&h&i&j&k&l&m&n&o&pq
//以上是 &a&b&c&d&e&f&g&h&i&j&k&l&m&n&o&pq的打印过程,会发现结果并不是设想的&a&b&c&...p&q
//而是pq中间没有&符号连接, 请注意上面打印的过程 (seqop,&p,q,&pq)  可以看出 &p,q是进行的分区间的计算!!!
//下面这种结果  和q做分区间计算的是&符号
res53: String = &a&b&c&d&e&f&g&h&i&j&k&l&m&n&o&p&q

由此推敲,scala的par对相同数据转化为并行集合,并不是固定的!!! 所以导致了aggregate的结果有所不同

reduce(聚合算子)
scala> var lst=Array(1, 2, 3, 1, 2, 4, 5, 6, 7, 9, 4)
lst: Array[Int] = Array(1, 2, 3, 1, 2, 4, 5, 6, 7, 9, 4)

scala> lst.reduce
   def reduce[A1 >: Int](op: (A1, A1) => A1): A1

scala> lst.reduce((v1,v2)=>v1+v2)//lst.reduce(_+_)
res54: Int = 44

由上面只单纯的计算数的累加,可以看出fold和reduce实际可以互相替换使用(在fold的初始值为0的情况下),下面说一下他们的区别

区别reducefold
初始值reduce初始值是集合元素的头或尾(left或者right)fold必须手动指定初始值
空集合操作reduce进行空集合操作会抛异常fold进行空集合操作结果为初始值
返回值reduce最终聚合的元素必须和调用Reduce方法的集合中元素一致fold最终折叠得到的元素类型可以和调用fold中的元素不一致(使用foldLeft)
底层实现reduce底层采用尾递归实现fold底层采用foreach实现
  • 字符统计
var arrs=Array("this is a demo","good good study","day day up")
arrs.flatMap(_.split(" "))
.groupBy(word=>word)
.toList
.map(tuple=>(tuple._1,tuple._2.size))
.sortBy(_._1)
.foreach(println)
  • 元组计算
var arrs=Array("this is a demo","good good study","day day up")
arrs.flatMap(_.split(" "))
.map((_, 1))
.groupBy(_._1)
.toList
.map(t=>(t._1,t._2.map(_._2).sum))
.sortBy(_._2)
.foreach(println)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值