美文网首页
Scala学习笔记

Scala学习笔记

作者: 小北觅 | 来源:发表于2018-10-15 23:22 被阅读56次

    这篇文章是我跟着视频学,再加上看博客总结的Scala关键知识点,用来开发Spark完全够用。

    第一节:基础

    • 变量声明 var val
    • 七种值类型(Byte,Char,Short,Int,Long,Float,Double)
    • 条件表达式
    val  y = if (x>1) 1 else "error"
    
    • Java中所有类的基类是Object,Scala中所有类的基类是Any

    • Unit相当于Java中的void

    • for循环

    1 to 10 返回 1,2,3...10
    1 until 10 返回1,2,3...9

    for(i <- 1  to  10){
      println(i)
    }
    
    for(i <- 1  until  10){
      println(i)
    }
    
    //for循环遍历集合
    var a = 0;
    val numList = List(1,2,3,4,5,6);
    
    // for 循环
     for( a <- numList ){
         println( "Value of a: " + a );
     }
    
    //嵌套for循环的高级写法&for循环过滤
    for (i<- 1 to 3; j<- 1 to 3  if(i!=j)){
        println(i*10+j)
    }
    
    • 方法和函数的声明以及方法转换成函数

    Scala 有方法与函数,二者在语义上的区别很小。Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。
    Scala 中使用 val 语句可以定义函数,def 语句定义方法。

    方法定义格式如下:

    def functionName ([参数列表]) : [return type] = {
       function body
       return [expr]
    }
    
        //声明求两数之和的方法:
       def addInt( a:Int, b:Int ) : Int = {
          var sum:Int = 0
          sum = a + b
    
          return sum
       }
    

    函数定义格式如下:

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

    第二节 数组、映射、元组、集合

    • 定长数组
    val  arr1 = new Array[Int](8)
    
    var arr2 = Array("java", "scala")
    //小括号下标
    println(arr2(0))
    
    • 变长数组
      注意要引入import scala.collection.mutable.ArrayBuffer
      然后使用ArrayBuffer 类
      追加操作: += ; ++= ; insert方法
      常用方法: reverse、delete、sum、max、min、sorted、yield关键字
    • 映射

    创建映射

    //用静态类创建
    val map1 =Map("scala"-> 1, "java"->2, "python"->3)
    //用元组创建
    val map2 = Map(("scala",1),("java",2),("python",3))
    

    获取值
    map1("scala")

    可变映射:

    //必须要引用这个
    import scala.collection.mutable.Map
    map1("scala")=6
    

    常用函数:getOrElse

    • 元组
      K-V对的集合
    val  t = ("scala", 100L,  3.14, ("spark",1))
    //取元组的第一个位置的值,“scala”
    t._1
    

    note:元组的取值下标从1开始。

    //这样可以用a,b,c,d去取元组的值
    val  t,(a,b,c,d) = ("scala", 100L,  3.14, ("spark",1))
    println(a)
    

    两个方法:
    toMap用于把数组(元素是元组)转换成不可变的Map
    zip拉链操作。用于把多个Array的值对应起来生成一个新的Array,新的Array内容为元组。

    arr1 = Array(24,25,26)
    arr2 = Array("a","b","c")
    arr1.zip(arr2)
    

    如果两个数组的长度不等,那么就会把长的数组的后面截取掉。

    • 集合

    Seq(序列)

    val list1 = List(1,2,3)
    val list2 = 0:: list1 //在List头部添加0
    val list3 = 0 +:list1 //也是在List头部添加0
    val list6 = list1 :+ 4 //在List尾部部添加4

    合并两个List:使用++ ++: :::
    可变List:使用ListBuffer

    Set(集合)
    集合有去重功能

    import scala.collection.immutable.HashSet
    val set1 = new  HashSet[Int ]()
    

    不可变集合可以使用++合并两个Set
    可变集合使用add +=追加元素
    可变集合使用 ++=合并两个集合
    可变集合使用 -= remove删除元素

    Map(映射)

    val  map1 = new  HashMap[String,Int]()
    map1("scala")=1
    map1 +=  (("java",2))
    map1. put("C++",5)
    map1 - ="java"
    map1 . remove("C++")
    

    第三节 函数式编程

    • lazy关键字修饰的是惰性变量

    • map


    • filter


    • flatMap


    • reduce


    • fold


    • aggregate


    • 交并差集
      union、intersect、diff

    • 实现wordcount

    object Test2{
      def main(args: Array[String]): Unit = {
        val  list = List("hello java hello scala hello python","hello java hello scala hello python")
        val words = list.flatMap(_.split(" "))
        val tuples = words.map( x=>(x,1) )
        val grouped = tuples.groupBy( (_._1) )
        val mapvalues = grouped.mapValues(_.size)
        println(grouped)
      }
    }
    

    第四节 面向对象

    • 创建类、属性
    package day01
    
    
    class Person{
      val id = "100"
      var name:String = _
      //只有本类才能访问,伴生对象也可以访问
      private var age = 120
      //只有本类才能访问,伴生对象访问不到
      private [this] val gender = "男"
    }
    //伴生对象
    object Person{
      def main(args: Array[String]): Unit = {
        val  p = new Person()
        //可以修改var变量
        p.age=200
        println(p.age)
      }
    }
    object Test1{
      def main(args: Array[String]): Unit = {
        val p = new Person
        //p.age出错,因为是private
        //p.age
      }
    }
    
    • 构造器、辅助构造器
    
    //主构造器的参数列表要放到类名的后面,和类名放在一起
    //此时的faceValue:Int只能在本类调用,伴生对象也无法调用,虽然没有用val或var修饰,但默认是val
    class StructDemo(val name:String, var age:Int, faceValue:Int=90) {
    
      var gender:String = _
    
      def getFaceValue():Int={
         return faceValue
      }
    
      //辅助构造器
      def this(name:String,age:Int,faceValue:Int,gender:String){
        this(name,age,faceValue)  //辅助构造器第一行必须先调用主构造器
        this.gender = gender
      }
    }
    
    object StructDemo{
      def main(args: Array[String]): Unit = {
        //val s = new StructDemo("ningning",26,98)
        //println(s.faceValue)  //访问不到,因为没用val或var修饰
        val s = new StructDemo("ningning",26,98,"女")
        println(s.name)
        println(s.age)
        val face = s.getFaceValue()
        println(face)
        println(s.gender)
    
      }
    }
    
    • 单例对象

    就是用object修饰的类
    创建对象的时候可以不用new
    直接 val test = Object1
    调用里面的方法直接类名.方法名即可

    • 伴生对象

    与类名相同,并且用object修饰的对象叫伴生对象
    类和伴生对象之间可以相互访问私有的方法和属性

    • apply、unapply方法

    apply方法经常用在伴生对象中,用来构造对象而不用显式地使用new。

    unapply是当做是伴生对象的apply方法的反向操作。apply方法接受构造参数,然后将他们变成对象。而unapply方法接受一个对象,然后从中提取值。unapply方法返回的是一个Option.

    object ScalaRunner {
      def main(args: Array[String]): Unit = {
        testApply2()
        testApplyUnApply()
        testCaseClass()
        testNumber()
        testUnapplyCheck()
      }
     
      private def testUnapplyCheck(): Unit = {
        "Hello World fdf" match {
          case Name(first, last@IsCompound()) => println(s"first: ${first}, last: ${last}")
        }
      }
     
      private def testNumber() = {
        val Number(n) = "12345"
        println(s"n: ${n}")
      }
     
     
      private def testCaseClass(): Unit = {
        val p: Person = Person("Sky", 20)
        p match {
          case Person(name, 20) => println(s"name: ${name}")
        }
      }
     
      private def testApplyUnApply() {
        val nameObj = Name("hello", "world")
        println(s"a: ${nameObj.a}, b: ${nameObj.b}")
     
        val Name(arg11, arg12) = "Hello World"
        println(s"arg11: ${arg11}, arg12: ${arg12}")
     
        val Name(arg21, arg22) = Name("hello", "world").toString
        println(s"arg21: ${arg21}, arg22: ${arg22}")
     
      }
     
      private def testApply(): Unit = {
        Name()
        (new Name) ()
        (new Name()) ()
        Name()()
     
      }
     
      private def testApply2(): Unit = {
        Name(1)
        (new Name()) (1)
        (new Name) (1)
        Name(1)(1)
      }
    }
     
    case class Person(val name: String, val age: Int)
     
    object Number {
      def unapply(input: String): Option[Int] = {
        try{
          Some(Integer.parseInt(input.trim))
        } catch {
          case ex: NumberFormatException => None
        }
      }
     
    }
     
     
    class Name {
      var a = ""
      var b = ""
     
      def this(a: String, b: String){
        this
        println("Call class construct.")
        this.a = a
        this.b = b
      }
     
      def apply() = {
        println("Call class apply.")
      }
     
      def apply(i: Int) = {
        println(s"Call class apply ${i}.")
      }
     
      override def toString: String = {
        a + " " + b
      }
     
    }
     
    object Name {
     
      def apply(): Name = {
        println("Call object apply.")
        new Name()
      }
     
      def apply(i: Int): Name = {
        println(s"Call object apply ${i}.")
        new Name()
      }
     
      def apply(a: String, b: String): Name = {
        println(s"Call object apply.")
        new Name(a, b)
      }
     
      def unapply(input: String): Option[(String, String)] = {
        println(s"Call object unapply.")
        val pos = input.indexOf(" ")
        if(pos == -1) None
        else Some((input.substring(0, pos), input.substring(pos + 1)))
      }
     
    }
     
    object IsCompound {
      def unapply(input: String) = {
        println(s"Call IsCompound unapply ${input}.")
        input.contains(" ")
      }
    }
    

    运行结果:

    • private关键字

    参考资料:Scala访问权限修饰符:private和private[this]

    作为参考和对比,首先从Java开始。
    在Java中,方法可以访问该类的所有对象的私有字段,例如:

    public class Person {
        private String name;
        private int age;        // 该字段无getter/setter方法
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        private String getName() {  // 该私有方法只是为了和Scala对比
            return name;
        }
        private void setName(String name) {  // 该私有方法只是为了和Scala对比
            this.name = name;
        }
        public boolean equals(Person other) {
            return this.name.equals(other.name) && this.age == other.age;
        }
    }
    

    Person类的equals方法内,能够直接访问其他Person对象的私有字段(other.name/other.age)。

    在Scala中,方法也可以访问该类的所有对象的私有字段,称为类私有字段;

    但在Scala中,允许定义更加严格的字段访问控制,通过private[this]来实现,称为对象私有字段,即只能在对象内部访问的字段,请看下图:

    在图中,job属于类私有字段,salary属于对象私有字段;
    方法内不能访问其他对象的对象私有字段(salary)
    Scala编译器分别对private和private[this] 都做了什么?
    首先对gao.Person.scala文件做一下修改,如下:

    class Person(val name:String, var age:Int) {
      private var job = "Programmer"
      private[this] var salary = 3000F
    }
    

    然后编译该文件,再使用javap查看编译后的Person.class文件,可以看到:

    $ javap -p target/scala-2.11/classes/gao/Person.class
    Compiled from "Person.scala"
        public class gao.Person {
        private final java.lang.String name;
        private int age;
        private java.lang.String job;  // private修饰,生成的private的getter/setter方法
        private float salary;               // private修饰,没有生成getter/setter方法
        public java.lang.String name();
        public int age();
        public void age_$eq(int);
        private java.lang.String job();                 // job字段的private的getter方法
        private void job_$eq(java.lang.String);  // job字段的private的setter方法
        public gao.Person(java.lang.String, int);
    }
    
    • 特质
      scala中的特质可以类比成Java中的接口:
      1.特质中定义的方法可以实现,【有了大括号的就是已经实现过的方法,例如下面Animal中的listen和run】;也可以不实现【例如Animal类中的speak方法】
    package cookBook.chapter8
    
    trait Animal{
      //没有实现
      def speak
    
      def listen: Unit ={
      }
      def run: Unit ={
        println("I'm running")
      }
    }
    
    class People extends  Animal{
      override def speak: Unit ={
        println("I'm speaking English")
      }
    }
    
    object People extends App{
      var  people = new People
      people.speak
      people.listen//don't have result
      people.run
    }
    

    运行结果如下:

    I'm speaking English
    I'm running

    同时,如果我们将上面类People中的speak给删除,会显示报错。class People must either be declared abstract or implements abstract members...。这就是说:

    2.如果一个类实现特质,那么必须实现特质中未定义的方法。否则这个类应该为抽象类。scala中有抽象类,但是更加倾向于使用特质。

    3.scala中可以实现一个类同时继承多个特质

    package cookBook.chapter8
    
    //轮胎
    trait tire{
      def run: Unit ={
        println("I can run fast")
      }
    }
    
    //方向盘
    trait SteeringWheel{
      def control: Unit ={
        println("I can control the cars'direction")
      }
    }
    
    //同时继承多个特质
    //使用with,后面可接多个with
    class Roadster extends  tire with SteeringWheel {
      def display(): Unit ={
        println("I'm a Roadster")
      }
    }
    
    //敞篷跑车
    object Roadster extends App{
      var roadster = new Roadster
      roadster.display()
      roadster.run
      roadster.control
    }
    

    运行结果如下:

    I'm a Roadster
    I can run fast
    I can control the cars'direction

    注:特质构造顺序
    1、首先调用超类的构造器

    2、然后调用特质构造器,特质构造器在超类构造器之后,类构造器之前

    3、特质由左到右被构造

    4、在每个特质当中,父特质先被构造

    5、如果多个特质公用一个父特质,而那个父特质已经被构造过了,则不会再被构造

    6、所有特质构造完毕,子类被构造

    介绍特质比较详细的一篇文章https://www.cnblogs.com/nowgood/p/scalatrait.html

    • 抽象类
    //抽象类
    abstract class Animal1{
      //抽象字段
      var name:String 
      var size:Int
      
      //抽象方法
      def walk
    }
     
    //抽象类实现类
    class Cat(var length:Int)extends Animal1{
      override var name = "cat"
      override var size = 100
      override def walk{
        println(this.name + ":" + this.size + ":" + this.length)
      }
      
    }
    object AbstractClassTest {
      
      def main(args: Array[String]): Unit = {
          val cat = new Cat(200)
          cat.walk
          println("name:" + cat.name)
          println("size:" + cat.size)
          println("length:" + cat.length)
      }
    }
    

    cat:100:200
    name:cat
    size:100
    length:200

    Scala抽象类不能被实例化,包含若干定义不完全的方法,具体的实现由子类去实现。

    第五章 模式匹配

    以下部分来自:菜鸟教程
    Scala 提供了强大的模式匹配机制,应用也非常广泛。
    一个模式匹配包含了一系列备选项,每个都开始于关键字 case。每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。以下是一个简单的整型值模式匹配实例:

    object Test {
       def main(args: Array[String]) {
          println(matchTest(3))
    
       }
       def matchTest(x: Int): String = x match {
          case 1 => "one"
          case 2 => "two"
          case _ => "many"
       }
    }
    

    执行以上代码,输出结果为:

    $ scalac Test.scala
    $ scala Test
    many

    match 对应 Java 里的 switch,但是写在选择器表达式之后。即: 选择器 match {备选项}。match 表达式通过以代码编写的先后次序尝试每个模式来完成计算,只要发现有一个匹配的case,剩下的case不会继续匹配。接下来我们来看一个不同数据类型的模式匹配:

    object Test {
       def main(args: Array[String]) {
          println(matchTest("two"))
          println(matchTest("test"))
          println(matchTest(1))
          println(matchTest(6))
    
       }
       def matchTest(x: Any): Any = x match {
          case 1 => "one"
          case "two" => 2
          case y: Int => "scala.Int"
          case _ => "many"
       }
    }
    

    执行以上代码,输出结果为:

    $ scalac Test.scala
    $ scala Test
    2
    many
    one
    scala.Int

    实例中第一个 case 对应整型数值 1,第二个 case 对应字符串值 two,第三个 case 对应类型模式,用于判断传入的值是否为整型,相比使用isInstanceOf来判断类型,使用模式匹配更好。第四个 case 表示默认的全匹配备选项,即没有找到其他匹配时的匹配项,类似 switch 中的 default。

    • 样例类

    使用了case关键字的类定义就是就是样例类(case classes),样例类是种特殊的类,经过优化以用于模式匹配。
    以下是样例类的简单实例:

    object Test {
       def main(args: Array[String]) {
           val alice = new Person("Alice", 25)
        val bob = new Person("Bob", 32)
           val charlie = new Person("Charlie", 32)
       
        for (person <- List(alice, bob, charlie)) {
            person match {
                case Person("Alice", 25) => println("Hi Alice!")
                case Person("Bob", 32) => println("Hi Bob!")
                case Person(name, age) =>
                   println("Age: " + age + " year, name: " + name + "?")
             }
          }
       }
       // 样例类
       case class Person(name: String, age: Int)
    }
    

    执行以上代码,输出结果为:

    $ scalac Test.scala
    $ scala Test
    Hi Alice!
    Hi Bob!
    Age: 32 year, name: Charlie?

    在声明样例类时,下面的过程自动发生了:
    构造器的每个参数都成为val,除非显式被声明为var,但是并不推荐这么做;
    在伴生对象中提供了apply方法,所以可以不使用new关键字就可构建对象;
    提供unapply方法使模式匹配可以工作;
    生成toString、equals、hashCode和copy方法,除非显示给出这些方法的定义。

    • 偏函数

    第六节 柯里化、隐式转换

    有关逆变协变
    https://blog.csdn.net/zero__007/article/details/52245475
    这篇文章是讲Java的。不过意思一样。
    文章中提到了PECS原则,就是什么时候使用逆变协变的。

    第七节 Actor和AKKA

    Scala笔记整理(九):Actor和AKKA

    相关文章

      网友评论

          本文标题:Scala学习笔记

          本文链接:https://www.haomeiwen.com/subject/pyxboftx.html