學好Spark/Kafka必須要掌握的Scala技術點(一)變數、表示式、迴圈、Option、方法和函式,陣列、對映、元組、集合

大資料學習與分享發表於2020-12-14

前言

Scala是以JVM為執行環境的物件導向的函數語言程式設計語言,它可以直接訪問Java類庫並且與Java框架進行互動操作。正如之前所介紹,Spark是用Scala語言編寫的,Kafka server端也是,那麼深入學習Scala對掌握Spark、Kafka是必備掌握技能。

本篇文章主要介紹,在學習、編寫Spark程式時,至少要掌握的Scala語法,多以示例說明。建議在用Scala編寫相關功能實現時,邊學習、邊應用、邊摸索以加深對Scala的理解和應用。

1. 變數、表示式、迴圈、Option、方法和函式

1.1 宣告變數

def main(args: Array[String]): Unit = {
    //使用val定義的變數值是不可變的,相當於java裡用final修飾的變數
    val i = 1
    //使用var定義的變數是可變的,在Scala中鼓勵使用val
    var s = "hello"
    //Scala編譯器會自動推斷變數的型別,必要的時候可以指定型別
    //變數名在前,型別在後
    val str: String = "hello"
}

 

1.2 表示式

1.2.1 條件表示式

def main(args: Array[String]): Unit = {
    val x = 1
    
    // 判斷x是否大於0,將最終結果賦給y,列印y
    // 二者等效, Scala語言強調程式碼簡潔
    //    var y = if(x > 0) {x} else {-1}
    //    val y = if(x > 0) x else -1

    //   支援混合型別表示式,返回值型別是Any
    //    var y = if(x > 0) x else "no"

    // 如果缺失else,相當於if (x > 0) 1 else ()
    //    scala表示式中有一個Unit類,寫作(),相當於java中void
    //    val y = if(x > 0) 1
    //    if和else if
    val f = if (x < 0) 0 else if (x >= 1) 1 else -1
    println(y)
}

 

 

1.2.2 塊表示式

def main(args: Array[String]): Unit = {
    val x = 0
    
     //  scala中{}可包含一系列表示式,塊中執行最終結果為塊的值
    val result = {
      if(x < 0)  -1  else if(x >= 1) 1  else "error"
    }
    println(result)
}

 

 

1.3 迴圈

Scala裡面while迴圈和Java中的while迴圈使用方式類似,這裡主要以for迴圈為例:

def main(args: Array[String]): Unit = {
    // 表示式1 to 10返回一個Range區間,每次迴圈將區間中的一個值賦給i
    for (i <- 1 to 3) {
      println(i)
    }

    //i代表陣列中的每個元素
    val arr = Array("a", 1, "c")
    for (i <- arr) {
      println(i)
    }
    
    //高階for迴圈
    //每個生成器都可以帶一個條件,注意:if前面沒有分號
    //相當於雙層for迴圈,i每獲得一個值對1to3進行全部遍歷並賦值給j然後進行條件判斷
    for (i <- 1 to 3; j <- 1 to 3 if (i != j)) {
      println(i + j)
    }

    //for推導式:如果for的迴圈體以yield開頭,則該迴圈會構建一個集合
    //    每次迭代生成集合中的一個元素 集合型別為Vector
    var v = for (i <- 1 to 3) yield i * i
    println(v)

    //遍歷一個陣列,to:包頭包尾;until:包頭不包尾
    for (i <- arr.length - 1) {
      println(arr(i))
    }
    for(i <- 0 until arr.length) {
      println(arr(i))
    }
}

 

 

1.4 Option型別

在Scala中Option型別樣例類用來表示可能存在或也可能不存在的值(Option的子類有Some和None)。Some包裝了某個值,None表示沒有值:

def main(args: Array[String]): Unit = {
    val map = Map("a"->1,"b"->2)
//根據key獲取value匹配match中的邏輯有值返回Some型別(已封裝資料),無值返回None
    val v = map.get("b") match {
      case Some(i) => i
      case None => 0
    }
    println(v)
    //更好的方式
    val value = map.getOrElse("c",0)
    println(value)
  }

 

 

1.5 方法和函式

Scala中的+、-、*、/、%等操作符的作用與Java一樣,位操作符&、|、^、>>、<<也一樣。但在Scala中:這些操作符實際上是方法。例如:a + b是a.+(b)方法呼叫的簡寫:a 方法 b可以寫成 a.方法(b)。

方法的返回值型別可以不寫,編譯器可以自動推斷出來,但是對於遞迴函式,必須指定返回型別。

def str = "a" 成立,定義一個字串

在函數語言程式設計語言中,函式可以像任何其他資料型別一樣被傳遞和操作:

偏函式:

//偏函式,它是PartialFunction[-A,+B]的一個例項,A代表引數型別,B代表返回值型別,常用作模式匹配(後文闡述)。
def func1: PartialFunction[String, Int] = {
    case "one" => 1
    case "two" => 2
    case _ => -1
  }
  def func2(num: String): Int = num match {
    case "one" => 1
    case "two" => 2
    case _ => -1
  }
  def main(args: Array[String]) {
    println(func1("one"))
    println(func2("three"))
}

 

 

2. 陣列、對映、元組、集合

2.1 陣列

import scala.collection.mutable.ArrayBuffer
//scala導包比如匯入scala.collection.mutable下所有的類:scala.collection.mutable._
object ArrayDemo {
  def main(args: Array[String]): Unit = {
    println("======定長陣列======")
    // 初始化一個長度為8的定長陣列,所有元素初始化值為0
    var arr1 = new Array[Int](8)
    // 底層呼叫的apply方法
    var arr2 = Array[Int](8)
    
    //toBuffer會將陣列轉換成陣列緩衝
    println(arr1.toBuffer) // ArrayBuffer(0, 0, 0, 0, 0, 0, 0, 0)
    println(arr1(2)) // 使用()來訪問元素
    
    println("=======變長陣列(陣列緩衝)======")
    val varr = ArrayBuffer[Int]()
    //向陣列緩衝尾部追加一個或多個元素(+=)
    varr += 1
    varr += (2, 3)

    //追加一個陣列用 ++=
    varr ++= Array(4, 5)
    varr ++= ArrayBuffer(6, 7)

    //指定位置插入元素-1和3;引數1:指定位置索引,引數2:插入的元素可以是多個
//    def insert(n: Int, elems: A*) { insertAll(n, elems) }
    varr.insert(0,-1,3)
    
    varr.remove(0) //刪除指定索引處的元素
    //從指定索引處開始刪除,刪除多個元素;參1:指定索引,參2:刪除個數
    varr.remove(0,2)
    
    // 從0索引開始刪除n個元素
    //  varr.trimStart(2)

    //從最後一個元素開始刪除,刪除指定個數的元素(length-n max 0)
    varr.trimEnd(2)
    )
    
    //reduce ==>非並行化集合呼叫reduceLeft
    //(((1+8)+3)+5)...
    println(varr.reduce((x,y)=> x+y))
    println(varr.reduce(_+_))
    println(varr.reduce(_-_))

Array("one","two","three").max //two,字串比較大小,按照字母表順序
Array("one","two","three").mkString("-")//以"-"作為陣列中元素間的分隔符one-two-three
Array("one","two","three").mkString("1","-","2")//1one-two-three2
    
    //參1:arr元素的陣列個數,參2:arr中每個陣列中元素個數
   val arr = Array.ofDim[Int](2, 3)
    arr.head arr.last //陣列的第一個和最後一個元素
  }
}

 

yield關鍵字將原始的陣列進行轉換會產生一個新的陣列,原始的陣列不變

def main(args: Array[String]) {
    //定義一個陣列
    val arr = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)
    //將偶數取出乘以10後再生成一個新的陣列
    val res = for (e <- arr if e % 2 == 0) yield e * 10
    println(res.toBuffer)
   
    //filter過濾接收一個返回值為boolean的函式,過濾掉返回值為false的元素
    //map將陣列中的每一個元素取出來(_)進行相應操作
    val r = arr.filter(_ % 2 == 0).map(_ * 10)
    println(r.toBuffer)
    
    //陣列元素求和,最大值,排序
    println(arr.sum+":"+arr.max+":"+arr.sorted.toBuffer)
}

 

 

2.2 對映

在Scala中,把雜湊表這種資料結構叫做對映,類似於Java中的Map。

在Scala中,有兩種Map:

不可變Map:scala.collection.immutable.Map(可以儲存一些配置或引數供多個執行緒訪問,保證執行緒安全,具體還要結合業務實際場景),內容不更改

可變Map:scala.collection.mutable.Map==>類似於Java中的HashMap,可以進行put、get、remove等操作,內容可變

map += ("c" -> 3)  map += (("d",4)) 增加元素 -=移除元素 +/-增加或移除一個元素並返回一個新的集合

注意:通常我們在建立一個集合時會用val這個關鍵字修飾一個變數,那麼就意味著該變數的引用不可變,該引用中的內容是不是可變還取決於這個引用指向的集合的型別

2.3 元組

對映是K/V對偶的集合,對偶是元組的最簡單形式,元組可以裝著多個不同型別的值,元組是不可變的

zip命令可以將多個值繫結在一起(將兩個陣列/集合的元素一一對偶):

注意:如果兩個陣列的元素個數不一致,拉鍊操作後生成的陣列的長度為較小的那個陣列的元素個數

對於元組val t = (1, 3.14, "Fred"),val (first, second, _) = t // second等於3.14

2.4 集合

Scala的集合有三大類:序列Seq、集Set、對映Map,所有的集合都擴充套件自Iterable特質。集合分可變(mutable)和不可變(immutable)兩種型別,immutable型別的集合初始化後長度和內容都不能改變(注意與val修飾的變數進行區別)

2.4.1 Seq/List

在Scala中列表要麼為空(Nil表示空列表)要麼是一個head元素加上一個tail列表。

9 :: List(5, 2)  :: 操作符是將給定的頭和尾建立一個新的列表【注意::: 操作符是右結合的,如9 :: 5 :: 2 :: Nil相當於 9 :: (5 :: (2 :: Nil))】

 

def main(args: Array[String]): Unit = {
    //建立一個不可變集合
    val lt = List(1, 2, 3)
    /*//新增元素到lt前面生成一個新的List
    val lt2 = ("a", -1, 0) :: lt
    val lt3 = lt.::(0)
    val lt4 = 0 +: lt
    val lt5 = lt.+:(0)
    println(lt + "==>" + lt2 + "==" + lt3 + "==" + lt4 + "==" + lt5)
    val lt6 = lt :+ 4
    println("新增元素到後面:"+lt6 )*/

    //合併兩個集合,lt0在lt前面
    val lt0 = List(4,5,6,7)
    val lt7 = lt0.union(lt)
    val lt8 = lt0 ++ lt
    println(lt7 +":"+lt8 )
    println("lt0在lt後面"+lt ++ lt0)
    //將兩個集合中的元素一一繫結,如果元素數不一致以較少元素集合為準
    println(lt0.zip(lt).toMap)

    //將lt0插入到lt前面生成一個新的集合
    println(lt0 ++: lt)
    println(lt.:::(lt0))
  }

def main(args: Array[String]): Unit = {
//    構建一個可變列表,初始有3個元素1,2,3   alt+enter導包
    val lst0 = ListBuffer[Int](1,2,3)
    //建立一個空的可變列表
    val lst1 = new ListBuffer[Int]
    //向lst1中追加元素,注意:沒有生成新的集合
    lst1 += 4
    lst1.append(5)
    println(lst1)
    //將lst1中的元素最近到lst0中, 注意:沒有生成新的集合
    println(lst0 ++= lst1)

    //將lst0和lst1合併成一個新的ListBuffer 注意:生成了一個集合
    println(lst0 ++ lst1)
    //將元素追加到lst0的後面生成一個新的ListBuffer
    val lst3 = lst0 :+ 5
    println(lst3)
}

def main(args: Array[String]): Unit = {
//    構建一個可變列表,初始有3個元素1,2,3   alt+enter導包
    val lst0 = ListBuffer[Int](1,2,3)
    //建立一個空的可變列表
    val lst1 = new ListBuffer[Int]
    //向lst1中追加元素,注意:沒有生成新的集合
    lst1 += 4
    lst1.append(5)
    println(lst1)
    //將lst1中的元素最近到lst0中, 注意:沒有生成新的集合
    println(lst0 ++= lst1)

    //將lst0和lst1合併成一個新的ListBuffer 注意:生成了一個集合
    println(lst0 ++ lst1)
    //將元素追加到lst0的後面生成一個新的ListBuffer
    val lst3 = lst0 :+ 5
    println(lst3)
  }
 def main(args: Array[String]): Unit = {
 /*   //建立一個List
    val lst0 = List(1,7,9,8,0,3,5,4,6,2)
    //將lst0中每個元素乘以10後生成一個新的集合
    val lst1 = lst0.map(_*10)
    val lst2 = for(i <- lst0) yield i * 10
    println(lst1 + ":" +lst2)
    //將lst0中的偶數取出來生成一個新的集合
    val lst3 = lst0.filter(_ % 2 == 0)
    val lst4 = for(i <- lst0 if(i % 2 == 0)) yield i
    println(lst3 + ":" +lst4)
    //將lst0排序後生成一個新的集合
    val lst5 = lst0.sorted//升序
    val lst6 = lst0.sortWith(_>_)//降序
    val lst7 = lst0.sortBy(x => x)//升序
    println(lst5 + ":" +lst6 + ":" +lst7)
    //反轉順序
    println(lst5.reverse)
    //將lst0中的元素4個一組,型別為Iterator[List[Int]]
    val it = lst0.grouped(4)
//    println(it.toBuffer)
    //將Iterator轉換成List
    val lst8 = it.toList
    //將多個list壓扁成一個List
    println(lst8.flatten)

    //先按空格切分,再壓平
    val lines = List("hello tom hello jerry", "hello jerry", "hello kitty")
    lines.map(_.split(" ")).flatten
lines.flatMap(_.split(" "))//
    //平行計算求和
    println(lst0.par.sum)
    println(lst0.par.reduce(_+_))//非指定順序
    println(lst0.par.reduceLeft(_+_))//指定順序

    //摺疊:有初始值(無特定順序)
    val lst11 = lst0.fold(100)((x, y) => x + y)
    //摺疊:有初始值(有特定順序)
    val lst12 = lst0.foldLeft(100)((x, y) => x + y) */
    //聚合
    val arr = List(List(1, 2, 3), List(3, 4, 5), List(2), List(0))
//    println(arr.flatten.sum)
    /*先區域性求和,再彙總
 _+_.sum:第一個下劃線是初始值和後面list.sum和,_.sum是list的和,非並行化時只初始化1次只攜帶1次
    _+_:初始值和list元素和的和 */
    val result = arr.aggregate(10)(_+_.sum,_+_)
    val res = arr.aggregate(10)((x,y)=>x+y.sum,(a,b)=>a+b)
    println(result+":"+res)

    val l1 = List(5,6,4,7)
    val l2 = List(1,2,3,4)
    //求並集
    val r1 = l1.union(l2)
    //求交集
    val r2 = l1.intersect(l2)
    //求差集
    val r3 = l1.diff(l2)
    println(r3)
  }

 

2.4.2 Set

def main(args: Array[String]): Unit = {
    //不可變Set
   /* val set1 = new HashSet[Int]()
    //將元素和set1合併生成一個新的set,原有set不變
    val set2 = set1 + 4
    //set中元素不能重複
    val set3 = set1 ++ Set(5, 6, 7)
    val set0 = Set(1,3,5) ++ set3
    println(set0)*/

    //建立一個可變的HashSet
    val set1 = new mutable.HashSet[Int]()
    //向HashSet中新增元素
    set1 += 2
    //add等價於+=
    set1.add(4)
    set1 ++= Set(1,3,5)
    println(set1)
    //刪除一個元素
    set1 -= 5
    set1.remove(2)
    println(set1)
  }

 


推薦文章:


大資料常用技術棧


Spark通識

相關文章