Scala基础学习

Scala

基础介绍

scala来源

  • Martin Odersky(马丁·奥德斯基)

特点

  • 面向对象

    • Scala中的每个值都是一个对象,包括基本数据类型(即布尔值、数字等)在内,连函数也是对象

    • 类可以被子类化,而且Scala还提供了基于mixin的组合

      • 类抽象机制的扩展有两种途径:一种途径是子类继承,另一种途径是灵活的混入机制
  • 函数式编程

    • 函数作为值来使用,匿名函数,高阶函数,嵌套多层函数,柯里化
    • Scala的case class及其内置的模式匹配相当于函数式编程语言中常用的代数类型
  • JVM,javascript

  • 静态语言

    • 具备类型系统,通过编译时检查,保证代码的安全性和一致性
  • 扩展性

    • Scala提供了许多独特的语言机制,可以以库的形式轻易无缝添加新的语言结构

      • 任何方法可用作前缀或后缀操作符
      • 可以根据预期类型自动构造闭包
  • 并发性

    • Actor并发模型,Actor是类似线程的实体,通过邮箱发收消息
    • Actor可以复用线程,因此在程序中可以使用数百万个Actor,而线程只能创建数千个

函数式编程

  • 任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用
  • 允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的
  • 函数也是值,同允许把函数本身作为参数传入另一个函数,还允许返回一个函数,函数是一等公民,以表达式为中心,无副作用,不修改状态,引用透明

静态语言

  • 静态编译语言:实现声明变量类型,类型不能改变,编译时检查; 动态编译语言:不用事先声明类型,随时可以赋
    值为其他类型

强类型和弱类型

  • 强类型语言:不同类型之间操作,必须强制类型转换为同一类型,print(‘a’+1): 弱类型语言:不同类型间可以操作,自动隐式转换

scala配置

  • 为什么学scala

    • 优雅
    • 速度快
    • 能融合到Hadoop生态圈
  • Scala解释器

    • REPL:Read(取值)-> Evaluation(求值)-> Print(打印)-> Loop(循环)
    • 计算表达式:在scala>命令行内,键入scala代码,解释器会直接返回结果给你。如果你没有指定变量来存放这个值,那么值默认的名称为res,而且会显示结果的数据类型,比如Int、Double、String
    • 内置变量:在后面可以继续使用res这个变量,以及它存放的值
    • 自动补全:在scala>命令行内,可以使用Tab键进行自动补全
  • Scala集成环境配置

编码规范

  • 文件名和编码

    • 所有源文件编码必须是 UTF-8
  • 特殊转义字符

  • 非ASCII字符

  • 代码书写规范

    • 列宽
    • 缩进
    • 括号
    • 空行
  • 注释风格

  • 命名规范

数据类型,方法和函数

变量

  • var

  • val

  • 变量声明一定要初始化

  • 变量推断

    • 声明变量时,可以不指定变量类型,编译器会根据赋值内容自动推断当前变量的类型
  • 多变量定义

    • val (a, b, c) = (1, 2, “a”)
    • var i3,i4 = 10
  • 关于 lazy

    • val
    • 只有val修饰的变量才能被lazy修饰;使用lazy定义变量后,只有在调用该变量时才会实例化这个变量的值。而且惰性变量只能是不可变变量
  • var和val的区别

    • 内容是否不变

      • 值类型,引用类型
    • val修饰的变量在编译后,等同于加上final

    • 是否可以有lazy修饰.val修饰的变量还可以用lazy修饰

  • 推荐val 使用val的好处: 1,更安全 2,代码可读性更高 3,资源回收更快,方法执行完,val所定义的变量即回收

数据类型

  • scala类型层级关系

    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vKjEnOKP-1593567105192)(C:\Users\Data\Desktop\1000phone\scala01.PNG)]
  • Unit是值类型,他只有一个实例对象()

  • Nothing是所有类型的子类,他没有一个具体的实例对象,一个常见的应用如:抛出异常、程序exit,无限循环

  • Nothing是所有类型的子类,也是Null的子类。Nothing没有对象,但是可以用来定义类型

  • Null是所有引用类型的子类,它只有一个实例对象null,主要用来和其他的JVM语言进行互操作

  • Scala数据类型的位数,不受具体OS的影响,以保证Scala程序的可移植性

数值类型

  • Scala没有基本类型与包装类型之分,这些类型都是类,有自己的属性和方法

字符串类型

  • 引入的三引号作为字符串的开始和结束符,内部的原始字符串可以包含换行、引号和特殊字符

  • stripMargin方法,管道符号(|)

  • 字符串的类型实际上是 Java String,它本身没有 String 类。 在 Scala 中,String 是一个不可变的对象,所以该对象不可被修改。这就意味着你如果修改字符串就会产生一个新的字符串对象

  • String INTERPOLATION(字符串插值)

    • printf
    • s:字符串插值
    • f:插值并格式化输出
    • raw:对字符串不作任何变换的输出

Unit和布尔类型

  • Unit为空类型,相当于void,使用()进行初始化
  • Boolean类型,只能为true或者false

其他类型

  • Any可以接收任意的基本类型和引用类型

  • AnyRef接收任意的引用类型

  • AnyVal接收任意的基本类型

  • null值只能被推断为Null类型,null代表空值,可以被赋值给任何

  • Nothing类型在Scala的类层级的最低端;它是任何其他类型的子类型。

  • 当一个函数,我们确定没有正常的返回值,可以用Nothing 来指定返回类型,这样有一个好处,就是我们可以把返回的值(异常)赋给其它的函数或者变量(兼容性)

  • Option

    • Option(选项)类型用来表示一个值是可选的(有值或无值)
    • Some
    • None

关于类型转换

  • 自动类型转换

  • 强制类型转换

  • 数值类型和字符串类型的转换

    • 数值类型转字符串类型
    • 字符串类型转数值类型

操作符

  • 算术操作符

    • +、-、* 、/、%
  • 关系操作符

    • < > <= >= != ==
  • 逻辑操作符

    • && || !
  • 位操作符

    • | & ^ ~ << >> >>>
  • 赋值运算符

    • = += -= *= /= %= <<= >>= &= ^= |=
  • 运算符优先级

  • 注意点

      1. a + b 等价于 a.+(b)
      1. Scala没有++,-- 可以用+=,-=代替
      1. 操作符都是方法的重载,是方法的调用

表达式

  • 表达式:一个具有执行结果的代码块。结果是具体的值或者()

  • 表达式和语句的区别:表达式有返回值,语句被执行。表达式一般是一个语句块,执行后,返回一个值

  • 不使用return语句,最后一个表达式即返回值

  • 条件表达式

    • 单分支

      • if (条件表达式) {执行代码块}
    • 双分支

      • if (条件表达式) {执行代码块1 } else { 执行代码块2 }
    • 多分支

      • if (条件表达式1) { 执行代码块1}
      • else if (条件表达式2) { 执行代码块2 }
      • else {执行代码块n }
  • 块表达式

    • {}中可以包含一系列表达式,块中最后一个表达式的值就是块的值
    • 块语句,最后一句是赋值语句,值是unit类型的
  • 混合类型表达式

  • 注意

    • 每个表达式都有一个类型
    • 条件表达式有值
    • 混合型表达式,结果是Any或者AnyVal
    • scala没有switch语句

循环

  • for循环

    • for循环语法结构:for (i <- 表达式/数组/集合(Range))

    • to和until

    • 高级for循环(嵌套循环加过滤功能)

      • for(i <- 1 to 3 ; j<- 1 to 3 if i != j)
    • for推导式,如果for循环的循环体以yeild开始,则该循环会构建出一个集合或者数组,每次迭代生成其中的一个值

      • for ( i <- 1 to 10 )yield i*10
  • while循环和do…while循环

    • while (条件语句){表达式}
    • do{ 表达式}while(条件语句)
  • 终止循环

    • (flag)用if实现continue功能,使用布尔变量实现break功能

      • var foundIt = false
      • while(i
    • Scala里面竟然没有break和contine关键字,其实不是这样的,Scala里面推荐使用函数式的风格解决break和contine的功能,而不是一个关键字

    • import util.control.Breaks._ breakable

定义方法

  • def add(x: Int, y: Int): Int = x + y

  • def add(x: Int, y: Int) = x + y

  • def add(x: Int, y: Int){x + y},没有返回值,一定要用大括号把方法体括起来

  • def addThenMultiply(x: Int, y: Int)(multiplier: Int): Int = (x + y) * multiplier

  • def name:String = System.getProperty(“user.name”)

  • 带有默认值的方法

    • def funcadd(str:String=“hello scala!”)

    • 调用

      • method1(b=2) //不能method1(2)
  • 可变参数或不定长参数

    • 可变参数一定是参数列表的最后一个参数

    • 函数内部,重复参数的类型是声明参数类型的数组,但是如果你给一个可变参数的方法传一个数组,是会报编译错误:type mismatch,可以使用数组名:_*的方式传参

    • def add(a:Int*)

      • add(1,2,3,4,5)
      • add(arr:_*)
  • 返回值

    • 方法的返回值类型可以不写,编译器可以自动推断出来,但是对于递归函数,必须指定返回类型
    • 方法的返回值默认是方法体中最后一行表达式 的值,当然也可以用return来执行返回值,但不推荐这么做。
    • 若使用 return 来制定函数的返回值,scala的类型推断将会失效,要显式指定返回值类型。
    • 方法也可以没有返回值(返回值是Unit)

定义函数

  • 给方法传递一个函数类型的参数

    • def foo(f: Int => String) = …
    • def bar(f: (Boolean, Double) => List[String]) = …
  • 函数可以看做是带有参数的表达式

  • 定义方式

    • val f1 = ((a: Int, b: Int) => a + b)
    • val f2 = (a: Int, b: Int) => a + b
    • val f3 = (: Int) + (: Int)
    • val f4: (Int, Int) => Int = (_ + _)
    • val f1:((Int,Int)=>Int)={(x,y)=>x+y}
    • val f2:(Int,Int)=>Int =(x,y)=>x+y
    • val f1 = new Function2[Int, Int, Int] {
      def apply(x: Int, y: Int): Int = if (x < y) y else x
      }
  • 匿名函数

  • 递归函数

  • 无参函数

方法和函数的区别

  • 方法和函数的定义语法不同
  • 方法一般定义在某个类、特质、或者object中
  • 方法可以共享使用所在类内的属性
  • 函数式编程语言中,函数是“头等公民”,可以调用它,也可以传递它,存放在变量中,或者作为参数传递给另一个函数

将方法转换成函数

  • 使用神奇的下划线 _

面向对象

面向对象的概念

  • 封装
  • 继承
  • 多态
  • 抽象

  • scala类特点
    • 在scala中,类并不用声明为public
    • scala源文件中可以包含多个类,所有这些类都具有公有可见性
    • 如果没有定义构造器,类会有一个默认的无参构造器
    • var修饰的变量,对外提供getter setter方法
    • val修饰的变量,对外提供getter方法,没有setter方法
    • 声明一个属性,必须显示的初始化,然后根据初始化数据的类型自动推断,属性类型可以省略
    • _表示一个占位符,编译器会根据你变量的具体类型赋予相应的初始值使用占位符,变量类型必须指定
    • 不同对象的属性是独立,互不影响,一个对象对属性的更改,不影响另外一个
  • 定义类的属性
    • var修饰
      • var name = “tom”
      • var nickName:String = _
      • var address = null -> var address:String = null
    • val修饰
      • val name = “tom”
      • val修饰的变量不能使用占位符
    • private var hobby: String = “旅游” 类私有字段,有私有的getter方法和setter方法,只能在类的内部使用 其伴生对象也可以访问
    • private[this] val cardInfo = “123456” 对象私有字段,访问权限更加严格的,Person类的方法只能访问到当前对象的字段
  • 自定义属性的get和set方法
    • 将成员变量定义为私有
    • 字段属性名以“_”作为前缀,如定义:_x
    • getter方法定义为:def x = _x
    • setter方法定义时,方法名为属性名去掉前缀,并加上后缀,后缀是:“x_=”
  • 定义类的构造方法
    • scala构造器作用是完成对新对象的初始化,构造器没有返回值
    • scala中构造分为主构造器和辅助构造器
    • 主构造的参数直接放置于类名之后
    • 定义类
      • private var name: String 私有字段,私有的getter和setter方法
      • var name: String 私有字段,公有的getter和setter方法
      • private val name: String 私有字段,私有的getter方法
      • val name: String 私有字段,公有的getter方法
    • 主构造器会执行类定义中的所有语句
    • 如果不带val和var的参数至少被一个方法使用,该参数将自动升级为字段,这时,name和price就变成了类的不可变字段,而且这两个字段是对象私有的,这类似于 private[this] val 字段的效果,
    • 否则,该参数将不被保存为字段,即实例化该对象时传入的参数值,不会被保留在实例化后的对象之中
    • 可以通过private设置私有的主构造器,私有主构造器无法调用,需要定义辅助构造器创建相应的对象
    • 辅助构造器名称为this,通过不同参数进行区分,每一个辅助构造器都必须以主构造器或者已经定义的辅助构造器的调用开始,即需要放在辅助构造器的第一行,辅助构造器的声明不能和主构造器的声明一致,会发生错误(即构造器名重复)

对象

  • 创建对象和访问属性
    • val | var 对象名 [:类型] = new 类型()
    • 对象名.属性名
    • 对象名.方法名
  • 单例对象
    • 在scala中没有静态方法和静态字段,但是可以使用object这个语法结构来达到同样的目的
    • 高效共享单个不可变的实例
    • 单例模式 单例对象虽然类似于Java中的工具类,但它是一个对象,可以把单例对象名看做一个贴在对象上的标签。
    • 类和单例对象的区别是,单例对象不能带参数,单例对象不能用new关键字实例化,所以没有机会传递给它实例化的参数
    • 单例对象在第一次访问的时候才会初始化
    • 不与伴生类共享名称的单例对象被称为独立对象,可以作为相关功能的工具类,或者scala应用程序的入口点
  • 伴生对象
    • 当单例对象与某个类同名时,它被称为类的伴生对象,类和伴生对象必须定义在一个源文件里,类称为该单例对象 的伴生类,类和他的伴生对象可以互相访问其私有成员
    • 类的伴生对象的功能特性并不在类的作用域,所以在类中不能直接用newUniqueNumber()调用伴生对象的方法
  • apply
    • 通常我们会在类的伴生对象中定义apply方法,当遇到类名(参数1,…参数n)时apply方法会被调用
    • apply方法用来做工厂,Array的创建方式
    • 调用apply方法创建一个对象,等价于CustomerID.apply(“Sukyoung”)
  • unapply 提取器
    • 带有unapply方法的对象,经常用在模式匹配或者偏函数中
    • 调用unapply方法,提取name信息
  • 注意
    • apply方法通常被称为注入方法,在类的伴生对象中做一些初始化的操作
    • apply方法的参数列表不需要和主类的构造器参数类别统一
    • unapply方法通常被称为提取方法,使用unapply方法可以提取固定数量的对象或值
    • unapply方法会返回一个Option类型,如果Option有值,内部会有一个Some对象来封装这些值,如果没有值,会返回None
    • apply方法和unapply方法都是被隐式的调用的
  • 应用程序对象
    • Scala程序都必须从一个对象的main方法开始,可以通过扩展App特质,不写main方法

继承

scala中,让子类继承父类,与Java一样,也是使用extends关键字

  • 提高了代码的复用性
  • 提高了代码的扩展性和维护性
  • 继承就代表,子类可以从父类继承父类的属性和方法(子类继承了所有的属性,只是私有的属性不能直接访问,需 要通过公共的方法去访问);然后子类可以在自己内部放入父类所没有,子类特有的属性和方法;使用继承可以有 效复用代码
  • 子类可以覆盖父类的属性和方法;但是如果父类用fifinal修饰,则该类是无法被继承的,属性和方法用fifinal修饰,属 性和方法是无法被覆盖的
  • 只有主构造器可以调用父类的构造器。辅助构造器不能直接调用父类的构造器
  • Override,Super
    • Scala中,如果子类要重写覆盖一个父类中的非抽象方法,则必须使用override关键字 override关键字可以帮助我们尽早地发现代码里的错误,比如:override修饰的父类方法的方法名我们拼写错了;比如要覆盖的父类方法的参数我们写错了;等等 此外,在子类覆盖父类方法之后,如果我们在子类中就是要调用父类的被覆盖的方法呢?那就可以使用super关键 字,显式地指定要调用父类的方法
  • 覆盖变量override

多态

  • 向上转型和向下转型

  • 类型检查和转换 isInstanceOf和asInstanceOf

    • 使用isInstanceOf判断对象是否是指定类的对象,如果是的话,则可以使用asInstanceOf将对象转换为指定类型,
    • 用asInstanceOf方法也可以将引用转换为子类的引用
  • 注意

    • 如果对象是null,则isInstanceOf一定返回false,asInstanceOf一定返回null

    • 如果没有用isInstanceOf先判断对象是否为指定类的实例,就直接用asInstanceOf转换,则可能会抛出异常

    • classOf获取对象的类名

    • class Location(xPosition: Int,yPosition: Int,var zLocation: Int) extends Point(xPosition,yPosition)

      • class Point(xPosition:Int, yPosition:Int) {

        var x: Int = xPosition

        var y: Int = yPosition

        println(“Point init”)

        def move(x:Int, y:Int): Unit ={

        this.x += x

        this.y += y

        println(“x轴坐标为:” + this.x)

        println(“y轴坐标为:” + this.y)

        }

        }

        类型检查和转换

        // 子类参数名称需要与父类参数名称对应

        class Location(xPosition: Int,yPosition: Int,var

        zLocation: Int) extends Point(xPosition,yPosition) {

        var z:Int = zLocation

        // 当方法名和参数列表相同时需要使用override关键字

        override def move(x: Int, y: Int): Unit = {

        // 可以使用super关键字调用父类方法

        super.move(x,y)

        println(“z轴坐标为:” + z)

        }

        }

      • object Location{

        def main(args: Array[String]): Unit = {

        val location = new Location(0,0,5)

        println(location.getClass) // class

        com.sand.test.Location

        println(location.isInstanceOf[Location]) // true

        println(location.isInstanceOf[Point]) // true

        val point = location.asInstanceOf[Point]

        println(point.getClass) // class

        com.sand.test.Location

        println(point.isInstanceOf[Point]) // true

        println(point.isInstanceOf[Location]) // true

        val point1 = new Point(3,4)

        println(point1.getClass) // class

        com.sand.test.Point

        println(point1.isInstanceOf[Point]) // true

        println(point1.isInstanceOf[Location]) // false

        val location2 = point1.asInstanceOf[Location]

        }

        }

特质 trait 和抽象类

  • 特质
    • 特质 (Traits) 用于在类 (Class)之间共享程序接口 (Interface)和字段 (Fields)。 它们类似于Java 8的接口。 类和对象 (Objects)可以扩展特质,但是特质不能被实例化,因此特质没有参数;
    • 在triat中可以定义抽象方法,就与抽象类中的抽象方法一样,只要不给出方法的具体实现即可
    • class 类名 extends 特质1 with 特质2 with 特质3
    • class 类名 extends 父类 with 特质1 with 特质2 with 特质3
    • 类继承trait后,必须实现其中的抽象方法,实现时不需要使用override关键字scala不支持对类进行多继承,但是支持多重继承trait,使用with关键字即可
    • Scala中的Triat可以定义具体变量,此时继承trait的类就自动获得了trait中定义的变量 但是这种获取变量的方式与继承class是不同的:如果是继承class获取的变量,实际是定义在父类中的,相当于我们 得子类获得到的是父类的引用地址;而继承trait获取的变量,就直接被添加到了类中;
    • 声明一个没有值的字段,定义抽象字段的时候,那么如果我们继承了这个接口,那么必须要重写实现这个字段,并且给予赋值;
    • 声明一个有方法体的方法 def log(msg:String)不需要abstract关键字修饰
      • 实现抽象方法,不需要override
    • 声明一个没有方法体的方法 def log(msg: String) { println(msg) }
    • 特质用来在类之间进行接口或者属性的共享。类和对象都可以继承特质,特质不能被实例化,因此也没有参数。 一旦特质被定义了,就可以使用extends或者with在类中混入特质
    • 多个叠加的特质:可以为类或者对象添加多个互相调用的特质,特质的执行顺序,取决于特质被添加的顺序
    • 特质的构造顺序
      • 调用超类的构造器
      • 特质构造器在超类之后,类构造器之前执行
      • 如果混入多个特质,按先后顺序构造
      • 每个特质中,父特质中先被构造
      • 如果多个特质共有一个父特质,父特质不会被重复构造
      • 所有特质构造完毕后,该类被构造
    • 在scala中,trait也可以继承自class,此时这个class就会成为所有继承该trait的类的父类
  • 抽象类
    • 抽象类抽象类基本语法
      • 通过abstract关键字标记不能被实例化的类。方法不用标记abstract,只要省掉方法体即可。抽象类可 以拥有抽象字段,抽象字段/属性就是没有初始值的字段
      • 抽象类的价值更多是在于设计,是设计者设计好后,让子类继承并实现抽象类(即:实现抽象类的抽象方法)
    • 抽象类 abstract
      • 声明一个有值的字段
      • 声明一个没有值的字段 val name: String
      • 声明一个有方法体的方法
      • 声明一个没有方法体的方法 def run: String
    • 如果某个类至少存在一个抽象方法或一个抽象字段,则该类必须声明为abstract
    • 注意
      • 抽象类不能被实例
      • 抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法 一旦类包含了抽象方法或者抽象属性,则这个类必须声明为abstract,抽象方法不能有主体不允许使用abstract 修饰。
      • 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法和抽象属性,除非它自己也声明为abstract 类。抽象方法和抽象属性不能使用private、fifinal 来修饰,因为这些关键字都是和重写/实现相违背的;
      • 抽象类中可以有实现的方法.
      • 子类重写抽象方法不需要override,写上也不会错

内部类

  • 内部类的使用规则与Java稍有不同,不允许外部类访问内部类的私有成员

  • class Outer {

    // outer相当于外部类Outer的别称

    outer => class Inner{

    ​ // 私有的方法不能被外部类访问

    ​ private def innerFun1(): Unit ={

    ​ // 使用别称.成员的方式访问外部类的成员

    ​ outer.outerFun()

    ​ println(“Inner - private - fun1”)

    ​ }

    ​ def innerFun2(): Unit ={

    ​ // 使用this关键字访问当前类的成员

    ​ this.innerFun1()

    ​ println(“Inner - public - fun2”)

    ​ }

    ​ class InnerIn{

    ​ def fun(): Unit ={

    ​ // 使用类名.this.成员的方式访问外部类的成员

    ​ Inner.this.innerFun1()

    ​ println(“InnerIn - public - fun”)

    ​ }

    ​ }

    }

    ​ def outerFun(): Unit ={

    ​ // 可以直接访问同级的内部类

    ​ val inner = new Inner

    ​ inner.innerFun2()

    ​ // 使用内部类实例来访问该内部类下的成员

    ​ val innerIn = new inner.InnerIn

    ​ innerIn.fun()

    ​ }

    }

样例类

  • 样例类是一中特殊的类,样例类是不可变的,可以通过值进行比较,可用于模式匹配

  • 特点

    • 样例类仍然是类
    • 样例类用case关键字进行声明。
    • 样例类是为模式匹配而优化的类构造器中的每一个参数都成为val——除非它被显式地声明为var(不建议这样 做)在样例类对应的伴生对象中提供apply方法,让你不用new关键字就能构造出相应的对象
    • 提供unapply方法让模式匹配可以工作
    • 将自动生成toString、equals、hashCode和copy方法(有点类似模板类,直接给生成,供程序员使用)
    • 除上述外,样例类和其他类完全一样。你可以添加方法和字段,扩展它们
  • 构造器中每一个参数都是val,除非显示地声明为var

  • 伴生对象提供apply ,让你不使用new关键字就能构造出相应的对象

  • 通过值对样例类对象进行比较

    • val point = Point(1, 2)

      val anotherPoint = Point(1, 2)

      val yetAnotherPoint = Point(2, 2)

    • if (point == anotherPoint) {

      println(point + " and " + anotherPoint + " are the same.")

      } else {

      println(point + " and " + anotherPoint + " are different.")

      }

      // Point(1,2) 和 Point(1,2)一样的.

      if (point == yetAnotherPoint) {

      println(point + " and " + yetAnotherPoint + " are the same.")

      } else {

      println(point + " and " + yetAnotherPoint + " are different.")

      }

      // Point(1,2)和Point(2,2)是不同的.

  • 样例类的拷贝

集合,元组和数组

集合概述

  • Scala 集合分为可变的和不可变的集合,不可变集合可以安全的并发访问

  • 可变集合可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。

  • 不可变集合,相比之下,永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变

    • scala.collection.immutable
    • scala.collection.mutable
  • Scala的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展自Iterable特质

  • 集合类结构组织图

    • 不可变

      • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wkLLpeaz-1593567105195)(C:\Users\Data\Desktop\1000phone\scala2.PNG)]
    • 可变

      • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7FRqfeeJ-1593567105198)(C:\Users\Data\Desktop\1000phone\scala3.PNG)]

数组

  • 数组元素内容要求类型一致

  • 定长数组,数组不可扩容Array

    • 数组创建

      • new
      • 直接使用Array
      • 区别
    • 访问 ()

    • 修改 =

    • 遍历

    • 查 for 增强for foreach

  • 变长数组,数组可扩容ArrayBuffer

    • 数组创建

      • new
      • 直接使用ArrayBuffer
      • 区别
    • 增 += ++=

      • append
      • insert
    • 删 -= --=

      • trimEnd
      • trimSart
      • remove
    • 查 for 增强for foreach

  • 定长数组与变成数组的转换

    • arr1.toBuffer
    • arr2.toArray
  • 数组方法

    • yield关键字
    • filter
    • map
    • sum,max,min
    • sorted,sortWith
  • 数组进阶

    • 二维数组

      • 矩阵
    • 多维数组

    • 不规则多维数组

    • var myMatrix = ofDimInt

    • val multiDimArr = new ArrayArray[Int]

    • for

元组Tuple

  • 元组的本质

    • Tuple2[Int, String]
    • Scala 支持的元组最大长度为 22。对于更大长度你可以使用集合,或者扩展元组
  • 创建访问元组

    • val t,(a,b,c) = (“a”,1,true)
    • val t = (“a”,1,true)
    • val tuple3 = new Tuple3(1, 3.14, “Fred”)
  • 元组访问

    • 注意元组元素的访问有下划线,并且访问下标从1开始
    • 按照索引访问元组的第一个元素,从0开始val value2 = tuple3.productElement(0)
  • 遍历

    • for
    • tuple1.productIterator.foreach(i => println(i))
  • 集合类拉链操作

    • zip
    • zipAll
    • zipWithIndex

列表List

  • 描述

    • 列表中的元素类型不可以不同。
    • 列表是有序的
  • 不可变列表import scala.collection.immutable._

    • 创建

      • Nil
      • List()
      • 所有的列表可以看做是由两个基础构造快Nil和中缀操作符::构成,::表示从列表前段扩展
      • 在Scala中列表要么为空(Nil表示空列表)要么是一个head元素加上一个tail列表
      • 9 :: List(5, 2) :: 操作符是将给定的头和尾创建一个新的列表
      • 注意::: 操作符是右结合的,如9 :: 5 :: 2 :: Nil相当于 9 :: (5 :: (2 :: Nil))
      • 列表中的元素可以是不同类型
    • 访问 ()

    • 遍历 for foreach map filter reduceLeft foldLeft

      • def :[B](z: B)(op: (A, B) => B): B = foldRight(z)(op)
      • def /:[B](z: B)(op: (B, A) => B): B = foldLeft(z)(op)
    • 列表追加

      • 0 :: Nil
      • 0 +: List()
      • List() :+ 0
      • List() ++ List()
      • List() ++: List()
      • List() ::: List()
    • 列表的拆分模式

      • val fruit = List(“apples”, “oranges”, “pears”)
      • val List(a, b, c) = fruit
      • val a :: b :: rest = fruit
    • 列表方法

      • head tail

        • last init
      • isEmpty concat length

      • fill

      • reverse

      • take

        • drop
      • splitAt

      • 拉链操作

      • 高阶方法

        • map flatMap foreach
        • filter partition find takewhile dropwhile span
        • sortWith
  • 可变列表 import scala.collection.mutable.ListBuffer

    • 创建

      • val lst0 = ListBufferInt
      • val lst1 = new ListBuffer[Int]
    • 追加

      • lst1 += 4
      • lst1.append(5)
      • lst ++= lst1
      • lst :+ 4

Set

  • 不可变Set import scala.collection.immutable.HashSet

    • 创建

      • val set = new HashSetInt
      • val set = Set(1,2,3)
    • 追加

      • ++
  • 可变Set import scala.collection.mutable

    • 创建

      • val mutableSet = Set(1,2,3)
    • 追加

      • +=
      • add
      • ++
    • 删除

      • -=
      • remove
    • 集合方法

      • tail head isEmpty
      • min max count iterator size
      • splitAt take takeRight toList
    • 交 并 差集

      • & insersect
      • &~ diff
      • union
  • 无序,不可重复

映射Map

  • map概述

    • 哈希表这种数据结构叫做映射
    • Map存储的内容是键值对(key-value),Scala中不可变的Map是有序的,可变的Map是无序的
  • 不可变Map scala.collection.immutable.Map

    • 创建

      • ->
      • (,)
    • 访问

      • ()

      • contains()

      • getOrElse

      • get

        • Option
  • 可变Map scala.collection.mutable.Map

    • 修改

      • update

        • key存在,则修改对应的值,key不存在,则添加键值对
      • map(key) = value

      • +=

      • ++=


      • -=
      • put
      • remove
    • 遍历

      • keySet
      • values
      • for(ele <- map)
      • for((k,v)<-map)
  • TreeMap

    • var tm = TreeMap(3 -> ‘x’, 1 -> ‘x’, 4 -> ‘x’)
    • +=

其他集合

  • Streams

    • Stream类似于list,它的元素是以懒惰的方式计算的,因此,一个stream可以用于存放无穷多个元素,但是其中的
      元素不是一次性生产出来的,而是动态生产的,末尾元素遵循lazy规则(使用的时候计算)
    • val str = 1 #:: 2 #:: 3 #:: Stream.empty
    • def fibFrom(a: Int, b: Int): Stream[Int] = a #:: fibFrom(b,a + b)
    • val fibs = fibFrom(1, 1).take(7)
    • fibs.toList
  • Vector

    • vector不可变
    • val vec = Vector(1, 2, 3)
    • vec updated (2, 4)
  • Stack

    • 后进先出
    • val stack = scala.collection.immutable.Stack.empty
    • push
    • top
    • pop
    • 不可变栈很少使用,因为可以使用list模拟栈的功能
  • 队列queue

    • 先进先出

集合的重要函数

  • sum/max/min/count

    • minBy & maxBy
  • filter flatten 交并差集

    • diff insersect union distinct
  • map flatMap

  • forall foreach take

  • partiton

  • fold foldLeft foldRight reduce reduceLeft reduceRight

其他

  • 线程安全的集合

    • 所有线程安全的集合都是以Synchronized开头的集合
  • 并行集合

    • Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算
  • 并行计算

    • Divide and conquer : 分治算法
    • Work stealin:算法
  • par

模式匹配

匹配常量和变量

  • 常量必须以大写字母开头
  • 变量模式类似于通配符,可以匹配任意对象。不过与通配符不同的地方在于,Scala把变量绑定在匹配的对象上。 确切的说单纯的变量模式没有匹配判断的过程,只是把传入的对象给起了一个新的变量名
  • case 5 => “hi!”
  • case somethingElse => "not zero: "+ somethingElse

匹配字符串

  • case “zhoudongyu” => println(“周冬雨”)

匹配数组元组列表…

  • 数组 case Array(x, y) => s"$x $y" case Array(0, _*) => “0…”
  • 元组 case (a, b, c) => println("matched "+ a + b + c)
  • 列表 case List(0, _*) => println(“found it”)

匹配类型

  • case s: String => println(s.toUpperCase)

匹配样例类

  • trait Node //抽象节点

    case class TreeNode(v:String, left:Node, right:Node) extends Node //具体的节点实现,有两个子节点

    case class Tree(root:TreeNode) //Tree,构造参数是根节点

    // Exiting paste mode, now interpreting.

    //构造一个根节点含有2个子节点的树:

    scala>val tree = Tree(TreeNode(“root”,TreeNode(“left”,null,null),TreeNode(“right”,null,null)))

    //如果我们期望一个树的构成是根节点的左子节点值为”left”,右子节点值为”right”并且右子节点没有子节点

    //那么可以用下面的方式匹配:

    scala> tree.root match {

    case TreeNode(,TreeNode(“left”,,_), TreeNode(“right”,null,null)) =>

    println(“bingo”) }

匹配Option

带守卫的匹配

  • 增加布尔表达式或者条件表达式使得匹配更具体

  • case SMS(number, _) if importantPeopleInfo.contains(number) =>

    “You got an SMS from special someone!”

偏函数

  • 了解偏函数

    • 对符合某个条件,而不是所有情况进行逻辑操作时,使用偏函数是一个不错的选择;
    • 被包在花括号内没有match的一组case语句,我们称之为偏函数;
    • 它是PartialFunction[A, B]的一个实例,A代表参数类型,B代表返回类型
    • Function 和 Partial Function
    • 对给定的输入参数类型,函数可接受该类型的任何值。换句话说,一个(Int) => String 的函数可以接收任意Int 值,并返回一个字符串。
    • 对给定的输入参数类型,偏函数只能接受该类型的某些特定的值。一个定义为(Int) => String 的偏函数可能不 能接受所有Int值为输入
    • PartialFunction 是个特质
    • 构建偏函数时,参数形式 [Any, Int]是泛型,第一个表示参数类型,第二个表示返回参数当使用偏函数时, 会遍历集合的所有元素,编译器执行流程时先执行isDefifinedAt()如果为true ,就会执行 apply, 构建一个新的Int 对象返回 执行isDefifinedAt() 为false 就过滤掉这个元素,即不构建新的Int对象.
    • map函数不支持偏函数,因为map底层的机制就是所有循环遍历,无法过滤处理原来集合的元素
    • collect函数支持偏函数
  • 偏函数方法

    • isDefifinedAt : 这个函数的作用是判断传入来的参数是否在这个偏函数所处理的范围内
    • orElse : 将多个偏函数组合起来使 用,效果类似case语句
    • andThen: 相当于方法的连续调用,比如g(f(x))
    • applyOrElse:它接收2个参 数,第一个是调用的参数,第二个是个回调函数。如果第一个调用的参数匹配,返回匹配的值,否则调用回调函 数
  • 偏应用函数

    • 偏函数应用指的是固化函数的一个或一些参数,从而产生一个新的函数。

    • val date = new Date

      val logWithDateBound = log(date, _ : String)

密封类

  • 在使用case 类来做模式匹配时,你可能想让编译器帮你确保已经列出了所有可能的选择。那么通常就要将通用超类声明为sealed

  • sealed abstract class Amount

  • case class Dollar(value:Double) extends Amount

    case class Rnb(value:Double) extends Amount

    case class Currency(value:Double,unit:String) extends Amount

  • 密封类的所有子类都必须在该密封类相同的文件中定义

  • 他们必须在Amount被声明的那个文件中完成。 如果类是密封的,那么在编译期所有的子类都是可知的,因而编译 器可以检查模式匹配语句的完整性

高阶函数

  • 如果一个函数的传入参数为函数或者返回值是函数,则该函数即为高阶函数

传入参数为函数

  • 函数是头等公民,和数字一样。不仅可以调用,还可以在变量中存放函数,也可以作为参数传入函数, 或者作为函数的返回值

传入参数为匿名函数

  • 在Scala中,你不需要给每一个函数命名,就像不必给每个数字命名一样,将函数赋给变量的函数叫做匿名函数

传入参数为方法

  • 隐式转换方法到函数 _
  • 在Scala中,方法和函数是不一样的,最本质的区别是函数可以做为参数传递到方法中

返回值为函数

  • def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String

方法的嵌套

  • def factorial(x: Int): Int = {

    ​ def fact(x: Int, accumulator: Int): Int = {

    ​ if (x <= 1) accumulator

    ​ else fact(x - 1, x * accumulator)

    ​ }

    ​ fact(x, 1)

    }

方法的多态

  • 方法可以通过类型实现参数化,类似泛型

  • def listOfDuplicates[A](x: A, length: Int): List[A] = {

    if (length < 1)

    Nil

    else

    x :: listOfDuplicates(x, length - 1)

    }

闭包

  • 闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量
  • 函数体内可以方法相应作用域内的任何变量。
  • 闭包通常来讲可以简单的认为是可以访问一个函数里面局部变量的另外一个函数

柯里化

  • 柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数并且返回接受余下的参数而且返回结果的新函数的技术
  • 当你调用 curriedSum (1)(2)时,实际上是依次调用两个普通函数(非柯里化函数),第一次调用使用一个参数 x, 返回一个函数类型的值,第二次使用参数y调用这个函数类型的值
  • def curriedSum(x:Int)(y:Int) = x + y
  • val onePlus = curriedSum(1)_ onePlus(2)

隐式转换和隐式参数

概念

  • 隐式转换和隐式参数是Scala中两个非常强大的功能,利用隐式转换和隐式参数,你可以提供优雅的类库,对类库 的使用者隐匿掉那些枯燥乏味的细节
  • 隐式的对类的方法进行增强

隐式转换机制

  • 行隐式转换时,需要遵守两个基本的前提
    • 不能存在二义性
    • 隐式操作不能嵌套使用
  • 隐式转换的时机
    • 当方法中的参数的类型与目标类型不一致时
    • 当对象调用所在类中不存在的方法或成员时,编译器会自动将对象进行隐式转换(根据类型)
  • 隐式解析机制 即编译器是如何查找到缺失信息的,解析具有以下两种规则
    • 首先会在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象)
    • 如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用 域是指与该类型相关联的全部伴生模块

隐式引用

  • scala会自己主动为每一个程序加上几个隐式引用
    • import java.lang._
    • import scala._
    • import Predef._

隐式类

  • 创建隐式类时,只需要在对应的类前加上implicit关键字
  • 要使用这个类, 只需将其导入作用域内
  • 使用隐式类时,类名必须在当前作用域内可见且无歧义,这一要求与隐式值等其他隐式类型转换方式类似
  • 只能在别的trait/类/对象内部定义
  • 构造函数只能携带一个非隐式参数
  • 在同一作用域内,不能有任何方法、成员或对象与隐式类同名

隐式方法和函数

  • 是指那种以implicit关键字声明的带有单个参数的函数,这种函数将被自动引用,将值从一种类型转换成另一种类 型
  • 使用隐含转换将变量转换成预期的类型是编译器最先使用 implicit 的地方。这个规则非常简单,当编译器看到类型 X而却需要类型Y,它就在当前作用域查找是否定义了从类型X到类型Y的隐式定义。

隐式参数

  • 在函数定义的时候,支持在最后一组参数使用 implicit ,表明这是一组隐式参数。在调用该函数的时候,可以不 用传递隐式参数,而编译器会自动寻找一个 implict 标记过的合适的值作为该参数

泛型类

协变

  • [-A]:逆变,作为参数类型。是指实现的参数类型是接口定义的参数类型的父类

逆变

  • [+B]:协变,作为返回类型。是指返回类型是接口定义返回类型的子类

上界

  • [B <: A] upper bounds 上限:B类型的上界是A类型,也就是B类型的父类是A类型

下界

  • [B >: A] lower bounds 下限:B类型的下界是A类型,也就是B类型的子类是A类型

视界

  • [B <% A] view bounds 视界:表示B类型要转换为A类型,需要一个隐式转换函数
  • 类型变量 A 必须是 类型 B的子类,或者A能够隐式转换到B

上下文界定

  • [B : A] context bounds 上下文界定(用到了隐式参数的语法糖):需要一个隐式转换的值

  • 上下文界定的形式为 T : M, 其中M 必须为泛型类, 必须存在一个M[T]的隐式值

  • class Pair_Context[T : Ordering](val first: T, val second: T){

    def smaller(implicit ord: Ordering[T]) =

    if(ord.compare(first, second) < 0) first else second

    }

包和引入

包的概念

包管理代码

包嵌套

包冲突

串联式包语句

文件顶部标记法

包对象

包可见性

包引入

重命名和方法隐藏

隐式引入

异常处理

异常抛出

异常捕获

文件和正则表达式

读取行

读取字符

读取单词

读取网络文件

写文件

正则表达式

正则表达式组

Actor和Netty

其他

解析

注解

定界延续

XML处理

类型系统

动态调用和宏与反射

GUI,scell

XMind: ZEN - Trial Version

你可能感兴趣的:(002.big,data,scala)