【Big Data--Scala】学习笔记(完整详细版)

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
大数据开发治理平台 DataWorks,不限时长
简介: Scala学习笔记

一、介绍

Scala(Scalable Language)是一门多范式的编程语言

Scala是一种纯面向对象语言,每个值都是对象

Scala也是一种函数式语言,其函数也能当成值来使用,Scala的case class及其内置的模式匹配相当于函数式编程语言中常用的代数类型

Scala是Spark的开发语言,而Spark是目前非常主流的大数据框架

二、环境配置

Scala的安装非常的简单,你只需要一款IDEAL软件即可

2.1 安装Scala插件

image.gif编辑

2.2 安装Scala SDK

image.gif编辑

image.gif编辑

三、基础语法

3.1 基础用法

    • 大小写敏感
    • 所有类名的第一个字母需要大写如MyHomeClass
    • 所有方法的第一个字母小写如myMethodAdd
    • main函数入口 def main(args: Array[String])
    • 标志符可以使用_、$、字母开头,后面可以使用数字或者字母
    • 注释符号 //
    • 每行的结束可以有选择性的使用;但是当一行中有多个语句的时候必须要使用;来进行分隔

    3.2 关键字

    abstract case catch class
    def do else extends
    false final finally for
    forSome if implicit import
    lazy match new null
    object override package private
    protected return sealed super
    this throw trait try
    true type val var
    while with yield
    - : = =>
    <- <: <% >:
    # @

    sealed:翻译密封的,指不能在类定义的文件之外定义任何新的子类

    implicit:翻译隐式的,即隐式转换中变量的标记

    trait:翻译特性,Scala中没有Interface,而是使用trait接口

    3.3 包

    Sclala使用package关键字来定义包,使用import关键字来导入包

    包的定义

    // 第一种方式类似Java
    package com.mp
    class HelloWorld
    // 第二种方式类似C#
    package com.mp{
    class HelloWorld
    }

    image.gif

    包的导入

    import java.mp._          // 引入包内所有成员
    import java.mp.Hello      // 引如包中的Hello成员
    import java.mp.{Hello,Bye}// 引入包中的Hello和Bye成员

    image.gif

    3.3 数据类型

    和Java一样一共有7种数据类型,Byte、Char、Short、Int、Long、Float、Double、Boolean和Unit

    注意:Unit表示无值,和其他语言中的void等同。返回值为()

    使用val定义的变量,其值是不能进行修改的

    val address = "CSDN"

    image.gif

    使用var定义的变量,其值是可以进行修改的

    var age = 18

    image.gif

    定义带有数据类型的变量

    val address:String = "CSDN"

    image.gif

    定义函数型变量

    var s:Unit=println("Hello")
    print(s)
    >>>
    Hello
    ()
    var s:Unit=println("Hello")
    s
    >>>
    Hello

    image.gif

    3.4 输出

    f 标志符可进行格式化的输出

    s 标志符可插入任何表达式的输出

    var name="Iphone20"
        var price=9999.999
        var business="Apple"
        println("公司"+business+"产品"+name)     // 字符串通过+进行相连
        println(s"${math.sqrt(2)}")
        println(f"商品:$name%s,价格:$price%.1f") // 带换行
        printf("商品 %s 价格 %.1f",name,price)   // 不带换行

    image.gif

    3.5 结构控制语句

    if

    var a=10
        // if语句
        var if_demo1=if(a>8) "Success" else "False"
        println(if_demo1)
        var if_demo2={
          if (a<=2) "D"
          else if (a >= 2 && a<=5  ) "C"
          else if (a>=6 && a<=8) "B"
          else "A"
        }
        println(if_demo2)
        >>>
        Success
        A

    image.gif

    for

    // for循环
        val arr = Array(1, 2, 3, 4, 5)
        // 全部遍历
        for (i <- arr) {
          println(i)
        }
        // 带下标
        for (i <- 0 to 4) {
          print(arr(i) + " ")
        };
        println()
        for (i <- 0 until 5) {
          print(arr(i) + " ")
        };
        println()
        // 条件
        for (i <- 0 until 5; if i % 2 == 0) {
          print(arr(i) + " ")
        };
        println()
        // 双重循环
        for (i <- 0 to 2; j <- 0 to 2; if i != j) {
          print("good")
        };
        println()
        //yield的用法
        var b=for (i<-1 to 15 if i%3==0) yield i
        println(b)
    >>>
    1
    2
    3
    4
    5
    1 2 3 4 5 
    1 2 3 4 5 
    1 3 5 
    goodgoodgoodgoodgoodgood
    Vector(3, 6, 9, 12, 15)

    image.gif

    3.6 方法定义

      • 方法体中的最后一行就是返回值
      • 一般将默认参数置后
      • 崇尚至简原则,return省略掉
      • 若函数体只有一行,花括号可省
      • 若返回值是字符串或者整数等可推断,则返回值类型可省
      • 含return则不可省返回值类型
      • 若返回值类型为Unit,则函数返回了其他类型也会无效
      • 若期望是无返回值类型,则可省略等号如def f(){printlen("hello")}
      • 若函数无参,声明了参数列表如f(),则调用的时小括号加不加均可
      • 若函数无参,没有声明参数列表如f,则调用的时候小括号一定不能加
      // 无参无返回值
          def f1(): Unit = {
            println("f1:无参无返回值")
          }
          f1()
          // 无参有返回值
          def f2(): String = {
            return "f2:无参有返回值"
          }
          println(f2())
          // 有参无返回值
          def f3(s: String): Unit = {
            println(s)
          }
          f3("f3:有参无返回值")
          // 有参有返回值
          def f4(s: String): String = {
            s
          }
          println(f4("f4:有参有返回值"))
          // 可变参数
          def f5(a: String*): Unit = {
            for (i <- a) {
              print(i)
            }
          }
          f5("f5:", "可变参数");
          println()
          // 默认参数
          def f6(a: Int = 1, b: Int = 2): Int = {
            a + b
          }
          println(f6())
      >>>
      f1:无参无返回值
      f2:无参有返回值
      f3:有参无返回值
      f4:有参有返回值
      f5:可变参数
      3

      image.gif

      3.7 隐式转换函数/类

      隐式转换函数是以implicit关键词声明的带有单个参数的函数, 该函数会自动应用,主要用于将值从一种类型转换为另一种类型

      // 将Float和Double自动转化为Int
          implicit def f1(d: Double): Int = {
            d.toInt
          }
          implicit def f2(f: Float): Int = {
            f.toInt
          }
          val num1: Int = 3.3
          val num2: Int = 3.2f
          println("num1+num2: " + (num1 + num2))
      >>>
      num1+num2: 6

      image.gif

      在使用类转换的时候如A转B,A类并非是完全转换成B,A会保留A的特点,因此可以将隐式转换看成类的继承

      object Main {
        def main(args: Array[String]): Unit = {
          // 将Float和Double自动转化为Int
          implicit def AinheritB(a: A): B = {
            new B
          }
          val a = new A
          a.fuc1()
          a.fuc2()
        }
      }
      class A {
        def fuc1(): Unit = {
          println("This is A")
        }
      }
      class B {
        def fuc2(): Unit = {
          println("This is B")
        }
      }
      >>>
      This is A
      This is B

      image.gif

      在Scala2.10之后提供了隐式类,使用implicit来标记类

      注意点

      1 隐式类的构造函数有且只能有一个

      2 隐式类不能是顶级的,只能在包对象、类对象、伴生对象里

      3 隐式类不能是Case Class

      4 作用域内不能有与之同名的标志符

      3.8 匿名函数

      没有名字的函数就是匿名函数,不关心名称,只关注逻辑处理。其实就是lambda函数

      形式 (参数) => {函数体}

      如 val y = (x:Double) => x*x

      1 (x:Double) => x*x 为匿名函数

      2 y为指向匿名函数的变量

      // 实现输入两个整数,返回两个整数之积的隐式函数
          val y=(n1:Int,n2:Int)=>{
            println("隐式函数!")
            n1*n2
          }
          println("隐式函数的类型是:"+y)
          println(y(2,3))、
      >>>
      隐式函数的类型是:Main$$$Lambda$16/0x0000000800c03000@3cbbc1e0
      隐式函数!
      6

      image.gif

      3.9 参数函数

      一个函数可以是另外一个函数的参数,这里的函数形式必须为函数名:(参数类型) => 返回值类型

      //  函数作为参数
          def f(f: (Int) => Int, x: Int, y: Int): Int = {
            f(x) + y
          }
          // 定义作为参数的函数
          def fun(x: Int): Int = {
            x * x
          }
          // 输出
          println(f(fun, 7, 8))
          //第二种方式,对于逻辑简单的函数直接写在函数参数位置
          println(f((x: Int) => {
            x * x
          }, 7, 8))
      >>>
      57
      57

      image.gif

      3.10 嵌套函数

      def fun1(x: Int) = {
            def fun2(y: Int) = {
              println("y=" + x * y)
            }
            fun2 _
          }
          fun1(3)(4)

      image.gif

      3.11 val函数

      val f1=(a:Int,b:Int)=>a+b
          val f2:(Int,Int)=>Int=(x,y)=>x+y
          val f3=(x:Int)=>println(x)
          val f4:(Int)=>Unit=(x)=>println(x)
          println(f1(1,2))
          println(f2(1,2))
          f3(3)
          f4(4)

      image.gif

      3.12 传值调用与传名调用

      Scala中函数A的参数可以是一个函数B。其实逻辑上是函数B会返回一个值,该值再放入函数A中。注意这里的传名是f1(f: =>Long)而传函数是f1(f:Long =>Long),二者是不一样的,传名的内容是一串代码

      def currentTime():Long={
            System.nanoTime()
          }
          def f1(f: =>Long):Unit={
            println("Time"+f)
          }
          def f2(time:Long)={
            println("Time"+time)
          }
          f1(currentTime)
          val time=currentTime
          f2(time)

      image.gif

      3.13 科里化函数

      科里化是将原来接受两个参数的函数变成新的接受一个参数的函数过程。直观的说就是将一个有两个参数的函数分割成两个只有一个参数的函数

      def substract1(x:Int)(y:Int)=x-y
          // substract2 返回的是一个匿名函数:(y:Int)=>x-y
          def substract2(x:Int)=(y:Int)=>x-y
          var result1=substract1(5)_
          var result2=substract2(5)(_)
          var result3=substract2(5)
          var s1=result1(2)
          var s2=result2(2)
          var s3=result3(2)
          println(result1)
          println(result2)
          println(result3)
          println(s1)
          println(s2)
          println(s3)
      >>>
      Main$$$Lambda$16/0x0000000800c021f8@50f8360d
      Main$$$Lambda$17/0x0000000800c025e8@2cb4c3ab
      3
      3

      image.gif

      3.14 闭包

      一个函数与其引用的变量组合成的一个整体叫做闭包

      def substract(x:Int)=(y:Int)=>x-y
          val f = substract(20)
          println(f(1))
          println(f(2))

      image.gif

      f是一个闭包,因为它有函数,有变量。而y只是属于它返回的匿名函数中的内容,因此y虽然不同,但并不影响f是个闭包

      问题1:编写一个函数,接受两个整数,返回它们的乘积。分别使用常规、科里化、闭包方式来完成

      def add1(x: Int, y: Int) = x * y
          def add2(x: Int)(y: Int) = x * y
          def add3(x: Int) = (y: Int) => x * y
      image.gif

      问题2:使用科里化函数和隐式类实现忽略大小写进行字符串比较

      object Main {
        def eq1(s1: String, s2: String): Boolean = {
          s1.equals(s2)
        }
        def main(args: Array[String]): Unit = {
          implicit class ImCompare(s1: String) {
            def check(s2: String)(f: (String, String) => Boolean): Boolean = {
              f(s1.toLowerCase, s2.toLowerCase)
            }
          }
          val s1 = "HELLO"
          val s2 = "hello"
          println(s1.check(s2)(eq1))
        }
      }
      image.gif

      3.15 偏函数

      表现形式为def f:partialFunction[A,B],其中A输入数据类型,B为输出数据类型。一般来说,只有在模式匹配中才可以看的见它

      // 常规写法
          def f1(s: String): Int = {
            if (s.equals("a")) 1
            else 2
          }
          // 偏函数
          def f2: PartialFunction[String, Int] = {
            case "a" => 1
            case _ => 2
          }

      image.gif

      3.15 _ 常见用法

      1 方法转函数

      def m1(x:Int,y:Int)=x+y    
        val f1=m1 _

      image.gif

      2 集合的每个元素

      val list=List(1,2,3,4)
      val list1=list.map(_* 10)

      image.gif

      3 获取元组Tuple中的元素

      val t=("hadoop",3.14,100)
          t._1
          t._2
          t._3

      image.gif

      4 模式匹配

      val word="hadoop"
      val result=word math{
          case "hadoop" => 1
          case "spark" => 2
          case _  => 0//以上都没有被匹配到才会被执行,相当于java中的default
      }

      image.gif

      5 队列

      val list=List(1,2,3,4)
      list match{
          case List(_,_*) => 1
          case _ => 2
      }

      image.gif

      6 导入包的时候

      //通配符,类似Java中的
      import java.util.*
      import scala.collection.mutable._
      //表示引入的时候将scala.collection.mutable包下面所有的类都导入
      import java.util.{Date =>_,_}
      //在java.util包下将Date屏蔽

      image.gif

      7、初始化变量

      var name:String=_
      //在这里,name也可以声明为null,例:var name:String=null。这里的下划线和null的作用是一样的。
      var age:Int=_
      //在这里,age也可以声明为0,例:var age:Int=0。这里的下划线和0的作用是一样的。

      image.gif

      8 函数中使用

      val set=setFunction(3.0,_:Double)
           println(set(7.1))
      //Scala中特有的“偏函数”用法

      image.gif

      9 传参

      val result=sum(1 to 5: _*)
      //当函数接收的参数不定长的时候,假如你想输入一个队列,可以在一个队列后加入“:_*”,因此,这里的“1 to 5”也可以改写为:“Seq(1,2,3,4,5)”。
      printArgs(List("a", "b"): _*)
      //将集合中的元素传给printArgs方法

      image.gif

      10 类型通配符

      def printList(list: List[_]): Unit ={
         list.foreach(elem => println(elem + " "))
      }//打印出所有List类型元素

      image.gif

      11  将函数赋给变量

      //如果尝试将函数直接赋值给一个变量,这个函数会被直接调用,并将调用的结果赋值给变量,如果在函数名称后面加上_,那么赋值的是函数体本身
      class Test {
          def fun = {
              // Some code
          }
          val funLike = fun _
      }

      image.gif

      12 参数展开

      def getConnectionProps = {
          ( Config.getHost, Config.getPort, Config.getSommElse, Config.getSommElsePartTwo )
      }

      image.gif

      13 简化函数

      val nums = List(1,2,3,4,5,6,7,8,9,10)
      nums filter (_ % 2 == 0)
      nums reduce (_ + _)
      nums.exists(_ > 5)
      nums.takeWhile(_ < 8)

      image.gif

      14 获取某数据类型的默认值

      var i:Int=_

      image.gif

      3.15 运算符

      符号 作用
      => 定义函数,函数变量 => 返回值
      <- 循环中将变赋给索引 for(i <- arr )
      -> 所有对象都有该方法,a->b返回一个二元组对象(a,b)
      _N 访问元组的第N元素

      3.16 Nil、Null、None、Nothing的区别

      名称 作用
      Nil 空的List
      Null AnyRe的子类,null是Null的唯一对象
      None Option的子类,若Option没有值则返回None
      Nothing 所有类型的子类,没有对象,但是可以定义类型,如果一个类型抛出异常,这个返回值就是Nothing

      四、进阶

      4.1 集合

      形式 内容
      1 长度固定,内容可变 Array
      2 长度可变的数组 ArrayBuffer
      3 长度可变的列表 ListBuffer
      4 不可变列表 List
      5 队列 Queue

      4.1.1 Array

      注意:取值是用()而不是[]

      定义与赋值

      // 定义不可变数组
          var arr:Array[Int]=new Array[Int](5)
          // Int类型自动赋值为0
          println(arr(1))
          // 定义数组后可指定数组的位置进行赋值
          arr.update(1,30)
          println(arr(1))
          // 添加元素

      image.gif

      循环遍历

      // 定义不可变数组
          var arr: Array[Int] = new Array[Int](5)
          var arr1: Array[Int] = new Array(1,2,3,4,5)
          // 方法一: 普通for循环
          for (i <- 0 until arr.length) {
            print(arr(i))
          }
          println()
          // 方法二: 简化for循环
          for (elem: Int <- arr) {
            print(elem)
          }
          println()
          // 方法三: foreach
          arr.foreach(print);
          println()
          arr.foreach(print(_));
          println()
          // 方法四: 迭代器
          var it: Iterator[Int] = arr.iterator
          while (it.hasNext) {
            print(it.next())
          }

      image.gif

      集合中的运算符

      运算符的本质:方法

      若运算符包含冒号,且冒号在后,则运算顺序为从右到左

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

      image.gif

      符号 操作 结果 位置 解释
      :: a::b List(List(1,2,3),4,5,6) 前插 将a当做一个元素插入到b前面
      +: a+:b List(List(1,2,3),4,5,6) 前插 同上
      :+ a:+b List(1,2,3,List(4,5,,6)) 后插 把b当做一个元素插入到a后面
      ++ a++b List(1,2,3,4,5,6) 拼接 a和b集合顺序合并
      ++: a++:b List(1,2,3,4,5,6) 拼接 同上
      ::: a:::b List(1,2,3,4,5,6) 拼接 同上

      注意:arr+:30和30:+arr都是错误,集合不能插在数字前也不能插在数字后

      4.1.2 ArrayBuffer

      var arr: ArrayBuffer[Int] = ArrayBuffer(1, 3, 5)
          // 添加元素,不可变的用符号,可变的用方法
          arr+=9
          arr.append(1)
          var arr1: ArrayBuffer[Int] = arr.+:(20)
          // 删除元素,双参数表示删除这个区间的数据
          arr.remove(1)
          arr.remove(1,2)
          // 修改元素
          arr.update(1,30)
          // 插入元素
          arr.insert(1,20)

      image.gif

      4.1.3  Array与ArrayBuffer的互相转换

      var arr: ArrayBuffer[Int] = ArrayBuffer(1, 3, 5)
          // 可变 ==> 不可变
          var arr1:Array[Int] = arr.toArray
          // 不可变 ==> 可变
          val arr2:mutable.Buffer[Int] = arr1.toBuffer

      image.gif

      4.1.4 ListBuffer

      var lb = ListBuffer(1,2,3)
          lb+=(6,7,8)

      image.gif

      规律:所有可变长的数据类型(带Buffer)添加元素可以直接使用+

      4.1.5 Queue

      队列是一个有序列表,遵循先入先出的原则。在 scala 中, 有 scala.collection.mutable.Queue 和 scala.collection.immutable.Queue , 一般来说,我们在开发中通常使用可变集合中的队列即scala.collection.mutable.Queue

      队列创建

      // 创建队列
          val q = new mutable.Queue[Any]

      image.gif

      方法 作用 案例
      + 添加元素 q+=5
      enqueue 入队尾 q.enqueue(100,100)
      + 添加列表元素 q+=List(1,2,3)
      ++ 将列表元素一个一个添加 q++=List(1,2,3)
      dequeue 出队头 q.dequeue()
      head 获取第一个元素 q.head
      last 获取最后一个元素 q.last
      tail

      获取初第一个元素之外的所有元素.

      可以级联使用

      q.tail

      q.tail.tail.tail

      4.2 运算符重载

      def main(args: Array[String]): Unit = {
          val s=new Student
          s+10
          s+10
          s.+(10)
          println(s.age)
        }
        class Student{
          var age=0
          def +(n:Int):Unit={
            this.age+=n
          }
        }
      >>> 30

      image.gif

      4.3 二维数组

      // 定义
          val arr = Array.ofDim[Int](3, 4)

      image.gif

      4.4 List

      // 定义列表
          // 方法一: 普通方法
          val ls1 = List(1,2,3,4)
          // 方法二: Nil代表空的List
          val ls2=1::2::3::4::5::Nil

      image.gif

      val l = List(1,2,3)

      image.gif

      目录
      相关文章
      |
      安全 程序员 API
      深入探索Scala的Option
      深入探索Scala的Option
      |
      机器学习/深度学习 运维 算法
      Data to be mined| 学习笔记
      快速学习 Data to be mined。
      121 0
      Data to be mined| 学习笔记
      |
      算法 Java 编译器
      【Scala】Scala之Control Structures(一)
       前面学习了Scala的Numbers,接着学习Scala的Control Structures(控制结构)。
      237 0
      【Scala】Scala之Control Structures(一)
      |
      Java Scala
      【Scala】Scala之Control Structures(二)
       前面学习了Scala的Numbers,接着学习Scala的Control Structures(控制结构)。
      76 0
      【Scala】Scala之Control Structures(二)
      |
      Java Scala
      Scala教程之:Option-Some-None
      Scala教程之:Option-Some-None
      |
      编译器 Scala 容器
      Scala的Higher-Kinded类型
      Scala的Higher-Kinded类型
      |
      Scala
      Scala学习笔记:how to convert a StringRDD to Array
      Scala学习笔记:how to convert a StringRDD to Array
      80 0
      Scala学习笔记:how to convert a StringRDD to Array
      |
      存储 NoSQL 分布式数据库
      带你玩转 Big Data
      Big Data(大数据)技术简析 Big Data是近来的一个技术热点,但从名字就能判断它并不是什么新词。毕竟,大是一个相对概念。历史上,数据库、数据仓库、数据集市等信息管理领域的技术,很大程度上也是为了解决大规模数据的问题。
      1710 0
      |
      Scala
      scala recursive value x$5 needs type
      recursive value x$5 needs type的原因是使用了一个类型不确定的变量,例如 val (id, name) = (id, getName(id))  其中id是个变量,其值还不确定, 就在getName(id)方法上使用.。
      3503 0