栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 大数据系统

Spark学习笔记[1]-scala环境安装与基本语法

Spark学习笔记[1]-scala环境安装与基本语法

Spark学习笔记[1]-scala环境安装与基本语法

  正所谓工欲善其事必先利其器,Spark的开发语言不是java而是scala,虽然都是运行于JVM,但是两门语言的基本特性还是有些不一样,这里说明一个概念,JVM不等于JAVA,任何语言只要能编译出符合JVM规范的class文件,都可以运行在JVM上

  相比于java,scala语言更加简简洁,且其实函数式编程语言,函数式变成语言的含义就是任何函数都是变量,有点类似于C++中的函数指针,由于语法很简洁,所以带来的问题就是相比于Java,用scala写的代码的代码可读性会差那么一点点

  变成语言说到底都只是一门工具,语言特性有差异,但是其能支持的功能基本上都大同小异,本文着重介绍一些scala语言的基本特性,详细的内容可以直接查看官网

1、准备工作(以windows系统为例)

1)、从官网下载scala的安装包,https://www.scala-lang.org/download/2.12.1.html , windows系统下载对应的msi文件,双击安装即可,安装过程中不能安装在默认的文件夹,默认文件夹是Program Files (x86),路径带空格,会报错

2)、添加环境变量,也可以在安装时由程序写入path

3)、打开命令行输入scala,如果能进入scala的编辑界面,则说明安装完成

4)、本文采用的集成开发工具是IDEA,scal可以集成到IDEA需要先安装scala的插件,具体可以参考博客:https://blog.csdn.net/zhujq_icode/article/details/82501559

2、基本语法及语言特性 2-1 main、class、 object和语句书写规则介绍

Java的类定义关键字是class,scala除了class外还有object关键字,主方法(main)主方法只能写在object定义的类中,示例代码如下:

object Collection {

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

Java的一个文件只能由一个主类,且主类名字和文件名必须一致,scala没有这个要求**object和class的区别:**scala中使用class定义类时不可有静态变量和静态方法(根本就没有static关键字),scala中static的功能可以用object类来实现(所以main必须在object中),但是该object类和class类必须定义在一个文件中,且必须同名,此时这个Object被称为"伴侣对象",例如同一个文件中的class A和Object A就是伴生对象,A中的变量就是class A的静态属性Object定义的类相当于静态的单例对象在Java中以分号标志一行语句,scala中分号可有可无,但是同一行如果有多句,需要用逗号分隔虽然scala允许文件名和类名不一致且一个文件可以有多个类,但是scala最终也需要编译成符合JVM规范的class文件,所以最终会由scala编译器生成类名和文件名一致的class类,所以同一个包下的不同文件不允许存在名称相同的类在scala中类体中可以执行业务逻辑,在Java中业务逻辑只能在方法中执行,在scala中定义在类体中的业务逻辑【也就是裸露的代码】会被scala编译器编译到默认构造函数,比如如下定义在类中裸露代码

class test{

  var a:Int = 3
  val name = "bbb"

  println(s"test.......$a")

  println(s"test......${a+1}")


}
2-2 变量定义
var/val 变量名:类型 = 值  //var定义变量,val定义常量,var定义的变量可以改值,val类似于final
2-3 构造函数

  scala中的可以写构造函数,如果不写,默认的构造函数就是由类体中的裸露代码构成,如果定义了个性化构造函数,定义方式如下

def this(参数名称:参数类型.....){
        //必须调用默认构造函数
        this()
     }
2-4 类名构造器

  除了常规的构造器,scala还有类名构造器,类名构造器的定义方式如下

 class A(var/val name[名称]:String[类型]){

     }
      
     new A(name="aaa")

类名构造器的基本特点如下:

1)、var/val可以省略,默认是val且为private2)、只有在类名构造器中的参数可以设置为var,其余方法中的参数都是val类型,且不允许设置成var类型3)、如果有类名构造器,又定义了自定义构造器,则在自定义构造器中调用默认构造器需要显示对其进行初始化,例如将A改成

calss A(name:String){
      
         def this(age:Int){
            this("aaa") //显示初始化类名构造器中的参数
         }
       }
2-5 流程控制 2-5-1 if/else

和java一样

 var i:Int = 0;
if(i == 0){
    println("i=0")
}else{
    println("aaaa")
}
2-5-2 while循环

和java一样,但是没有++这种自增的语法,直接用+=1替代

 var i = 0
          while(i<10){
            println(i)
            i +=1
          }
2-5-3 for循环

scala不支持for(int i=0;i<10;i++)这种语法,只支持增强的for循环,类似于java 中就是for(a:迭代器, 使用scala实现for(int i=0;i<10;i++)的语法是:

 for(i <- 0 to (9,1)){ //包含9
              println(i)
            }


for(j <- 0 until (10,1)){//不包含10
              println(j)
            }

循环表达式后面可以跟判断条件,例如满足某一个条件才执行循环体

 for(i <- 0 to (9,1) if (i%2==0)){
              println(i)
            }

双层for循环直接使用更加简洁,例如打印乘法表

  for(i <- 1 to 9 ;j <- 1 to 9 if (i >= j) ){
              print(s"$j * $i = ${i*j} t")
              if(i==j){
                println("")
              }
2-6 函数 2-6-1 常规函数

函数定义

      def 函数名(形参列表【都是形参名称:形参类型的格式】):返回值类型 = {

      }

如果函数无返回值,则返回值类型是Unit,函数的返回值类型可以用return,也可以直接将变量写在最后一行即可,例如

     def test1(): Int ={
        var i = 3
        //return i
        i
      }

如果函数无返回值,则返回值类型是Unit,函数的返回值类型可以用return,也可以直接将变量写在最后一行即可,例如

2-6-2 匿名函数
      
      var y= (形参列表) =>{
         函数体
      }
        
      //或者
      var y:(Int,Int)=>Int = (a:Int,b:Int)=>{
         a+b
      }

y:(Int,Int)=>Int叫函数的签名,可以作为函数的形参存在,调用匿名函数和调用普通函数差不多,就是y(形参)

2-6-3 嵌套函数(函数中定义函数)
 def test1(a:String):Unit = {
        def test2():Unit = {
          println(a)
        }
        test2()
      }
        
      test1("hello")
2-6-4 偏应用函数
        def fun07(date:Date,tp:String,msg:String): Unit ={
          println(s"$datet$tpt$msg")
        }
        
        var info = fun07(_:Date,"info","ok") //固定了后面两个参数,第一个参数"_"是占位
        
        info(new Date())
2-6-5 可变参数函数
 def fun08(a: Int*): Unit = {
          for (elem <- a) {
            println(elem)
          }
                    //函数作为参数
          a.foreach(println) //打印a的每一个原数,foreach接收一个形参的函数,返回值是泛型,println只有一个形参,返回值为Unit
          }
          
fun08(8)
println("-------------")
fun08(1,2,3,4)
2-6-6 高阶函数

函数作为参数或者返回值

       //函数作为参数,y:(Int,Int)=>Int就是所需函数格式,接收两个参数,返回一个Int数据
        def compute(a:Int,b:Int,y:(Int,Int)=>Int):Int = {
          y(a,b)
        }
        
        println(compute(3,4,(a:Int,b:Int)=>{a+b}))
        println(compute(3,4,(a:Int,b:Int)=>{a*b}))
        println(compute(3,4,_ % _))
        
        println("--------------------------------")
        //函数作为返回值
        def factory(op:String):(Int,Int)=>Int ={
          if(op.equals("+")){
            (a:Int,b:Int)=>{a+b}
          }else{
            (a:Int,b:Int)=>{a*b}
          }
        }
        
        var addFunc = factory("+")
        var mulFunc = factory("*")
        
        println(addFunc(3,4))
        println(mulFunc(3,4))
2-6-7 柯理化

又称为多参数列表,感觉有点抽象,形式如下

      def func09(a:Int)(b:String): Unit = {
        println(s"$at$b")
      }
      func09(5)("hello")

是不是感觉很多余,直接定义def func09(a:Int,b:String)不就好了,主要用途

1)、用于接收可变参数列表类型不一致时使用

def func09(a:Int*)(b:String*): Unit = {
        //      println(s"$at$b")
              a.foreach(println)
              b.foreach(println)
            }
            func09(5,6)("hello","word")
// 当然可以用 def func09(a:Any*)实现,但是此时就无法控制传入的参数类型

2)、**隐式参数:**如果要指定参数列表中的某些参数为隐式(implicit),应该使用多参数列表 2-7 集合框架

1)、 使用java的集合框架,虽然是java写的,但是已经编译成字节码,都是运行在JVM上,所以scala可以使用java的类库

//数组 var arr01 = Array(1,2,3,4) println(arr01(0)) //用小括号取对应索引,[]在scala是泛型参数 var arr02 = Array[Int](1,2,3,4) //链表 var list01 = List(1,2,3,4,5) //不可变List list01.foreach(println) //可变List var list02 = new ListBuffer[Int]() list02.+=(32) //添加元素,++ ++:等操作符的含义见https://blog.csdn.net/z1941563559/article/details/88751099 //集合 set //可变和不可变 //元组 var t2 = new Tuple2(11,"sssss") //2 代表可以有2个元素,最多可以Tuple22 println(t2._1) //取值 //迭代 val iterator = t2.productIterator //拿到迭代器 iterator.foreach(println) //Tuple2在scala描述的就是键值对 map import scala.collection.mutable.Map val map01:Map[String,Int] = Map(("a", 33), "b" -> 22, ("c", "44")) val keys:Iterable[String] = map01.keys map01.put("d",444) val value = map01.get("a").getOrElse("aa") // get("a")返回的是Option类型,Option内部有两个值,none和some,有值就是返回some,没有值返回none,再通过一层取到值 //集合操作 //map方法,接收一个函数,一进一出 val list = List(1,2,3,4,5) val list02 = list.map((x: Int) => (x * x)) list02.foreach(println) //reduce方法,接收一个函数,多进一出 list.reduce((a:Int,b:Int)=>(a+b)) //内部调用的是reduceLeft,从左到右累加,初始值是0 //flatMap方法,集合展开 val list03 = List("hello word","hello jeje") val strings = list03.flatMap((x: String) => { x.split(" ") }) strings.foreach(println) 2-8 迭代器

为了避免一次将大量数据直接加载到内存导致内存溢出,数据计算领域大量使用了迭代器模式,只需要保存指向真实数据的指针,通过对指针的迭代迭代数据,举个例子说明一下使用迭代器迭代scala的列表

        val list03 = List("hello word","hello jeje","hehe hhhhhh")
        val iter:Iterator[String] = list03.iterator

        val strings = iter.flatMap((x: String) => {
          x.split(" ")
        }) //返回的也是迭代器,没有发生实际的计算
    
    //    strings.foreach(println) //迭代元素,发生计算,最终调用的是iter的next和hasnext方法,可以看下源码
    
        val tuples = strings.map((_, 1))
        //strings是迭代器,且已经在调用strings.foreach后指向末尾,再对其进行map迭代,已经无法输出元素
        tuples.foreach(println)
2-9 高级特性 2-9-1 trait

类似于接口【编译后确实是接口】,用于多继承

         trait A {
           def say(): Unit={
             println("1")
           }
         }
    
         trait B {
           def sayB():Unit={
             println("2")
           }
    
           def sayB2():Unit
         }
    
         class Person(name:String) extends A with B {
           def hello(): Unit = {
             println(s"$name say hello")
           }
    
           override def sayB2(): Unit ={
             println("3")
           }
         }
    
         object traitTest {


           def main(args: Array[String]): Unit = {
             val p = new Person("ssss")
             p.sayB2()
           }


         }
2-9-2 case class

样例类,主要用于模式匹配,和普通类不一样的是样例类的比较是比较值而不是引用,所以如下的a和a2是相等的

         //类似于工厂,只要构造实例的值一样,出厂的产品就相同
         case class Dog(name:String,age:Int){
    
         }


         object caseClassTest {
           def main(args: Array[String]): Unit = {
             val a = new Dog("hashiqi", 18)
             val a2 = new Dog("hashiqi", 18)
             println(a.equals(a2))
           }
2-9-3 match 模式匹配

感觉上像是一个增强的switch,不仅可以对值进行匹配,还能对类型进行匹配

         val tup:(Double,Int,String,Char) = (1.0, 2, "aaa", 'a')
    
         val iter = tup.productIterator
    
         val res = iter.map((x:Any)=>{
           x match{
             case 1.0 => println("1.0")  //匹配值
     //        case 2 => println("2")
             case o:Int => println(s"$o is Int") //匹配类型,o就是传入的x
             case o:String => println(s"$o is String")
             case _ => println("default") //默认情况,也就是switch的default规则
           }
         })
    
         while(res.hasNext){
           res.next()
         }
2-10 偏函数

根据对应的规则处理数据返回对应值

         //第一个位置是传入参数,第二个参数是返回值类型
         def test:PartialFunction[Any,String]={
           case "hello" => "val is Hello"
           case x:Int => s"$x is Int"
           case _ => "none"
         }
    
         println(test(44))
         println(test("hello"))
         println(test('a'))
2-11 隐式转换

隐式转换的作用是对现有的已经编译好的类进行增强,假设现在使用的是java的linkList

             val list01 = new util.linkedList[Int]()
             list01.add(1)
             list01.add(2)
             list01.add(3)

需要对其进行遍历,但是java的linkList没有foreach方法,可以通过以下方法对其进行包装

1)、 封装方法

   def foreach[T](linkedList: util.linkedList[T],f:(T)=>Unit)={ //T是泛型参数
                  val iter = linkedList.iterator()
                  while(iter.hasNext){
                    f(iter.next())
                  }
                }
    
                foreach(list01,println)

2)、 封装类

class ListEx[T](linkedList: util.linkedList[T]){
    
            def foreach(f:(T)=>Unit)={
              val iter = linkedList.iterator()
              while(iter.hasNext){
                f(iter.next())
              }
            }
    
          }
    
              //    用类封装
              val listex = new ListEx(list01)
              listex.foreach(println)

3)、 同样使用类对其封装,使用隐式转换方法对原有集合进行增强

//隐式转换方法,名称无所谓,类型要对
              implicit def tran[T](linkedList: util.linkedList[T]):Unit={
                 new ListEx(linkedList)
              }
    
              list01.forEach(println)

4)、 使用隐式转换类

implicit class tran[T](linkedList: util.linkedList[T]) {
                def foreach(f: (T) => Unit) = {
                  val iter = linkedList.iterator()
                  while (iter.hasNext) {
                    f(iter.next())
                  }
                }
              }
    
              list01.forEach(println)

使用隐式转换可以在不修改源码的情况下对类功能进行增强,除了隐式转换函数和类,还有隐式转换参数,如下

implicit val aa:String = "aaa"
def aaaa(implicit aaa:String):Unit={ //代表参数可传可不传,不传的话,会从程序中定义的implicit变量寻找类型相匹配的填入,如寻找到多个则报错
    
                  println(aaa)
                }
    
//调用方式
                aaaa("bbb")
                aaaa
    
                //如果此时函数改成
                def aaaa(implicit aaa:String,bbb:Int)
                //虽然有一个String类型的隐式变量,但是调用aaaa时也不能只穿bbb参数,必须同时传入或者不传,若想实现只传入bbb的功能,需要用到柯理化(多参数列表),将函数定义成
                def test01(bbb:Int)(implicit aaa:String):Unit ={
                  println(s"$aaa ----> $bbb")
                }
    
                test01(10)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/729101.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号