1. 程式人生 > >[大資料] Scala 速學手冊1

[大資料] Scala 速學手冊1

Scala 基礎介紹

1 Scala

1.1 什麼是Scala

Scala是一種多正規化的程式語言,其設計的初衷是要整合面向物件程式設計和函數語言程式設計的各種特性。Scala運行於Java平臺(Java虛擬機器),併兼容現有的Java程式。

1.2為什麼要學Scala

  1. 優雅
  2. 速度快:開發速度快;靜態編譯,執行速度快
  3. 學習大資料元件

2 Scala基礎

2.1 宣告變數

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

2.2 常用型別

有7種數值型別Byte、Char、Short、Int、Long、Float和Double(無包裝型別)和一個Boolean型別

2.3 條件表示式

object ConditionDemo {
  def main(args: Array[String]) {
    val x = 1
    //判斷x的值,將結果賦給y
    val y = if (x > 0) 1 else -1
    //列印y的值
    println(y)

    //支援混合型別表示式
    val z = if (x > 1) 1 else "error"
    //列印z的值
    println(z)

    //如果缺失else,相當於if (x > 2) 1 else ()
    val m = if (x > 2) 1
    println(m)

    //在scala中每個表示式都有值,scala中有個Unit類,寫做(),相當於Java中的void
    val n = if (x > 2) 1 else ()
    println(n)

    //if和else if
    val k = if (x < 0) 0
    else if (x >= 1) 1 else -1
    println(k)
  }
}

2.4 塊表示式

object BlockExpressionDemo {
  def main(args: Array[String]) {
    val x = 0
    //在scala中{}中課包含一系列表示式,塊中最後一個表示式的值就是塊的值
    //下面就是一個塊表示式
    val result = {
      if (x < 0){
        -1
      } else if(x >= 1) {
        1
      } else {
        "error"
      }
    }
    //result的值就是塊表示式的結果
    println(result)
  }
}

2.5 迴圈語法

有for 和while 兩種用法 for迴圈常用 。
for迴圈語法結構:for (i <- 表示式/陣列/集合)

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

    //for(i <- 陣列)
    val arr = Array("a", "b", "c")
    for (i <- arr)
      println(i)

    //高階for迴圈
    //每個生成器都可以帶一個條件,注意:if前面沒有分號
    for(i <- 1 to 3; j <- 1 to 3 if i != j)
      print((10 * i + j) + " ")
    println()

    //for推導式:如果for迴圈的迴圈體以yield開始,則該迴圈會構建出一個集合
    //每次迭代生成集合中的一個值
    val v = for (i <- 1 to 10) yield i * 10
    println(v)

  }

}

2.6 呼叫方法和函式

Scala中的+ - * / %等操作符的作用與Java一樣,位操作符 & | ^ >> <<也一樣。只是有一點特別的:這些操作符實際上是方法。例如:

a + b

是如下方法呼叫的簡寫:

a.+(b)

a 方法 b可以寫成 a.方法(b)

2.7 定義方法和函式

2.7.1 定義方法

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

scala> def m1(x:Int, y: Int) : Int = x * y

2.7.2 定義函式

scala> val fun1 = (x: Int, y :Int) => x + y

2.7.3 函式和方法的區別

在函數語言程式設計語言中,函式是“頭等公民”,它可以像任何其他資料型別一樣被傳遞和操作
案例: 定義一個方法 ,然後傳遞一個函式

object MethodAndFunctionDemo {
  //定義一個方法
  //方法m2引數要求是一個函式,函式的引數必須是兩個Int型別
  //返回值型別也是Int型別
  def m1(f: (Int, Int) => Int) : Int = {
    f(2, 6)
  }

  //定義一個函式f1,引數是兩個Int型別,返回值是一個Int型別
  val f1 = (x: Int, y: Int) => x + y
  //再定義一個函式f2
  val f2 = (m: Int, n: Int) => m * n

  //main方法
  def main(args: Array[String]) {

    //呼叫m1方法,並傳入f1函式
    val r1 = m1(f1)
    println(r1)

    //呼叫m1方法,並傳入f2函式
    val r2 = m1(f2)
    println(r2)
  }
}

2.7.4 方法轉換成函式(神奇的下劃線)

scala> def m1 (x: Int, y: Int) : Int = x * y
scala> val f1 = m1 _

3 陣列·對映·元組·集合

3.1 陣列

1.定長和變長陣列

object ArrayDemo {

  def main(args: Array[String]) {

    //初始化一個長度為8的定長陣列,其所有元素均為0
    val arr1 = new Array[Int](8)
    //直接列印定長陣列,內容為陣列的hashcode值
    println(arr1)
    //將陣列轉換成陣列緩衝,就可以看到原陣列中的內容了
    //toBuffer會將陣列轉換長陣列緩衝
    println(arr1.toBuffer)

    //注意:如果new,相當於呼叫了陣列的apply方法,直接為陣列賦值
    //初始化一個長度為1的定長陣列
    val arr2 = Array[Int](10)
    println(arr2.toBuffer)

    //定義一個長度為3的定長陣列
    val arr3 = Array("hadoop", "storm", "spark")
    //使用()來訪問元素
    println(arr3(2))

    //////////////////////////////////////////////////
    //變長陣列(陣列緩衝)
    //如果想使用陣列緩衝,需要匯入import scala.collection.mutable.ArrayBuffer包
    val ab = ArrayBuffer[Int]()
    //向陣列緩衝的尾部追加一個元素
    //+=尾部追加元素
    ab += 1
    //追加多個元素
    ab += (2, 3, 4, 5)
    //追加一個數組++=
    ab ++= Array(6, 7)
    //追加一個數組緩衝
    ab ++= ArrayBuffer(8,9)
    //列印陣列緩衝ab

    //在陣列某個位置插入元素用insert
    ab.insert(0, -1, 0)
    //刪除陣列某個位置的元素用remove
    ab.remove(8, 2)
    println(ab)

  }
}

2.遍歷陣列
a)增強for迴圈

b)好用的until會生成腳標,0 until 10 包含0不包含10

object ForArrayDemo {

  def main(args: Array[String]) {
    //初始化一個數組
    val arr = Array(1,2,3,4,5,6,7,8)
    //增強for迴圈
    for(i <- arr)
      println(i)

    //好用的until會生成一個Range
    //reverse是將前面生成的Range反轉
    for(i <- (0 until arr.length).reverse)
      println(arr(i))
  }
}

3.陣列轉換

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

#定義一個數組
scala> val arr = Array(1,2,3,4,5,6)

#用yield生產新的陣列
scala> val res = for(e <- arr) yield e * 2

# map函式更加好用
scala> arr.map(_ * 2)
object ArrayYieldDemo {
  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的函式
    //map相當於將陣列中的每一個元素取出來,應用傳進去的函式
    val r = arr.filter(_ % 2 == 0).map(_ * 10)
    println(r.toBuffer)

  }
}

4. 陣列常用方法
在Scala中,陣列上的某些方法對陣列進行相應的操作非常方便

scala> val arr = Array(2,5,1,4,3)

#求和
scala> arr.sum

#最大值
scala> arr.max

#排序
scala> arr.sorted

3.2 對映

在Scala中把雜湊表這種資料結構叫做對映

1.構建對映

#第一種構建方式 用箭頭
scala> val scores = Map("tom" ->85,"jerry"->99)

#第二種構建方式 用元組
scala>cal scores = Map(("tom,85),("jerry",99))

2.獲取和修改對映中的值

#獲取值
scala> scores("jerry")

#好用的getOrElse
#如果對映有值,返回對映的值,否則返回預設值
scala> scores.getOrElse("suke",0)

注意:在Scala中,有兩種Map,一個是immutable包下的Map,該Map中的內容不可變;另一個是mutable包下的Map,該Map中的內容可變

#匯入mutable包
scala> import scala.collection.mutable.Map

#val 定義的變數意味著變數的引用不變,但是Map的內容可變
scala> val scores =Map("tom" -> 80)

#修改Map中的內容
scala>scores("tom") = 88

#用+= 向原來的Map中追加元素
scala> scores += ("kitty” -> 99)

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

3.3 元組

1.建立元組

#定於元組時用小括號將多個元組包起來,元素之間用逗號分開,元素的型別可以不一樣,元素的個數可以任意多個
scala> val t = ("hadoop,3.14,15926)

2.獲取元素中的值

#同時賦值
scala> val t,(a,b,c) = ("hadoop",3.14,15926)

#獲取元組中的元素可以使用下劃線加腳標,但是注意元組中的元素的腳標是從1開始的
scala> val r1 = t._1
scala> val r2 = t._2

3.將對偶的集合轉換成對映

scala> val arr = Array(("tom",88))

#toMap可以將對偶轉換成對映
scala> arr.toMap

4拉鍊操作

scala>val scores = Array(88,95,80)
scala>val name = Array("tom","jerry","kitty")

#使用zip將多個值繫結在一起
scala> ns = name.zip(scores)

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

3.4 集合-List

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

1不可變序列

不可變的序列 import scala.collection.immutable._
在Scala中列表要麼為空(Nil表示空列表)要麼是一個head元素加上一個tail列表。

9 :: List(5, 2) :: 操作符是將給定的頭和尾建立一個新的列表

注意::: 操作符是右結合的,如9 :: 5 :: 2 :: Nil相當於 9 :: (5 :: (2 :: Nil))

object ImmutListDemo {

  def main(args: Array[String]) {
    //建立一個不可變的集合
    val lst1 = List(1,2,3)
    //將0插入到lst1的前面生成一個新的List
    val lst2 = 0 :: lst1
    val lst3 = lst1.::(0)
    val lst4 = 0 +: lst1
    val lst5 = lst1.+:(0)

    //將一個元素新增到lst1的後面產生一個新的集合
    val lst6 = lst1 :+ 3

    val lst0 = List(4,5,6)
    //將2個list合併成一個新的List
    val lst7 = lst1 ++ lst0
    //將lst1插入到lst0前面生成一個新的集合
    val lst8 = lst1 ++: lst0

    //將lst0插入到lst1前面生成一個新的集合
    val lst9 = lst1.:::(lst0)

    println(lst9)
  }
}

2可變的序列 import scala.collection.mutable._

import scala.collection.mutable.ListBuffer

object MutListDemo extends App{
  //構建一個可變列表,初始有3個元素1,2,3
  val lst0 = ListBuffer[Int](1,2,3)
  //建立一個空的可變列表
  val lst1 = new ListBuffer[Int]
  //向lst1中追加元素,注意:沒有生成新的集合
  lst1 += 4
  lst1.append(5)

  //將lst1中的元素最近到lst0中, 注意:沒有生成新的集合
  lst0 ++= lst1

  //將lst0和lst1合併成一個新的ListBuffer 注意:生成了一個集合
  val lst2= lst0 ++ lst1

  //將元素追加到lst0的後面生成一個新的集合
  val lst3 = lst0 :+ 5
}

3.5 集合-Set

1不可變Set

import scala.collection.immutable.HashSet

object ImmutSetDemo extends App{
  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,4) ++ set1
  println(set0.getClass)
}

2可變Set

import scala.collection.mutable

object MutSetDemo extends App{
  //建立一個可變的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)
}

3.6 集合-Map

import scala.collection.mutable

object MutMapDemo extends App{
  val map1 = new mutable.HashMap[String, Int]()
  //向map中新增資料
  map1("spark") = 1
  map1 += (("hadoop", 2))
  map1.put("storm", 3)
  println(map1)

  //從map中移除元素
  map1 -= "spark"
  map1.remove("hadoop")
  println(map1)
}

實踐

用scala實現單機版本的wordcount程式

scala> val lines = List("hello world","hello scala")

#List[String] = List(hello, world, hello, scala)
scala >val words = lines.flatMap(_.split" ")

#res: List[(String, Int)] = List((hello,1), (world,1), (hello,1), (scala,1))
scala>val wordAndOne = words.map((_,1))

#scala.collection.immutable.Map[String,List[(String, Int)]] = Map(scala -> List((scala,1)), world -> List((world,1)), hello -> List((hello,1), (hello,1)))
scala> val group = wordAndOne.groupBy(_._1)

#scala.collection.immutable.Map[String,List[(String, Int)]] = Map(scala -> List((scala,1)), world -> List((world,1)), hello -> List((hello,1), (hello,1)))
#第一種方法  使用mapValues方法
scala>val res = group.mapValues(_.size)

#第二種方法  在mapValues中使用fold 函式
scala> val res = group.mapValues(_.fold(0)(_+_._2))

#第三種方法   使用map方法
scala>val res = group.map(t => (t._1,t._2.size))



scala> val lines = List("hello world","hello scala")

#第一種方法
scala> val res = lines.flatMap(_.split(" ")).map((_,1)).groupBy(_._1).mapValues(_.size)

#第二種方法
scala> val res = lines.flatMap(_.split(" ")).map((_,1)).groupBy(_._1).mapValues(_.fold(0)(_+_._2))

#第三種方法
scala> val res = val res = lines.flatMap(_.split(" ")).map((_,1)).groupBy(_._1).map(t => (t._1,t._2.size))