大數據處理編程神器--Scala,確定不學習一下嗎?

Scala字符串

Scala 集合

1. 數組

2. list

3. set

4. Map

4.2 map遍歷

方式一: foreach

方式二: 迭代器

方式三: for循環

5. 元組

trait特性

模式匹配match-case

併發 Actor Model

Actor的特徵:

  Actor與Actor之間通信:

Scala隱式轉換系統

隱式值

隱式視圖

隱式類


Scala字符串


  Scala中字符串也是分爲兩種: 可變長度的StringBuilder和不可變長度的String, 其操作用法與Java幾乎一致.

  接下來, 通過代碼來查看常用方法

//定義字符串
val str1 = "Hello Scala"
var str2 = "Hello Scala"
var str2_1 = "hello scala"

//字符串比較
println(str1 == str2)
println(str1.equals(str2))
println(str1.equalsIgnoreCase(str2_1))
//上述三個比較全部返回true

//按字典順序比較兩個字符串
println(str1.compareTo(str3))
//按字典順序比較兩個字符串,不考慮大小寫  
println(str1.compareToIgnoreCase(str3))

//從0開始返回指定位置的字符  
println(str1.charAt(6))
//追加
println(str2.concat(" Language"))
//是否以指定的後綴結束
println(str1.endsWith("la"))
//使用默認字符集將String編碼爲 byte 序列
println(str1.getBytes)
//哈希碼
println(str1.hashCode)
//指定子字符串在此字符串中第一次出現處的索引
println(str1.indexOf("ca"))
//字符串對象的規範化表示形式
println(str1.intern())
//指定子字符串在此字符串中最後一次出現處的索引
println(str1.lastIndexOf("al"))
//長度
println(str1.length)
//匹配正則表達式
println(str1.matches("d+"))
//替換字符
println(str1.replace('a','o'))
//根據字符切割, 需要注意Scala中從數組中取元素使用小括號
println(str1.split(" ")(1))
//是否以指定字符串開始
println(str1.startsWith("Hel"))
//截取子字符串
println(str1.substring(3))
println(str1.substring(3,7))
//大小寫
println(str1.toLowerCase())
println(str1.toUpperCase())
//去空格
println(str1.trim)

//使用StringBuilder
val strBuilder = new StringBuilder
//拼接字符串
strBuilder.append("Hello ")
strBuilder.append("Scala")
println(strBuilder)
//反轉
println(strBuilder.reverse)
//返回容量
println(strBuilder.capacity)
//指定位置插入
println(strBuilder.insert(6,"Spark "))



Scala 集合


1. 數組


  Java中使用 new String[10]的形式可以創建數組, 但Scala中創建數組需要用到Array關鍵詞, 用[ ]指定數組中元素的泛型, 取值使用小括號(index).

//創建Int類型的數組, 默認值爲0
val nums = new Array[Int](10)
//創建String類型的數組, 默認值爲null
val strs = new Array[String](10)
//創建Boolean類型的數組, 默認值爲false
val bools = new Array[Boolean](10)
//通過索引遍歷數組,給元素賦值
for (index <- 0 until nums.length) nums(index) = index + 1
//數組遍歷,編碼的逐步簡化
nums.foreach ( (x: Int) => print(x + " ") )
println()
nums.foreach ( (x => print(x + " ")) )
println()
nums.foreach(print(_))
println()
nums.foreach(print)

  foreach函數傳入一個函數參數, 由於Scala支持類型推測, 可以將參數函數的參數類型省略; 在參數函數中, 該函數的參數只出現一次, 因爲可以使用下劃線_代替(如果有多個可以使用_.1/_.2); 最後由於Scala語言的靈活性, 只需傳入print這個函數也會遍歷打印整個集合.

  創建二維數組分兩步: 創建一個泛型爲數組的數組, 然後對這個數組遍歷,

val secArray = new Array[Array[String]](5)
for (index <- 0 until secArray.length){
  secArray(index) = new Array[String](5)
}

//填充數據
for (i <- 0 until secArray.length;j <- 0 until secArray(i).length) {
  secArray(i)(j) = i * j + ""
}

secArray.foreach(array => array.foreach(println))



2. list


  Scala中列表的定義使用List關鍵詞. List集合是一個不可變的集合. 下面來看創建List已經list調用的方法.

//創建列表
val list = List(1,2,3,4,5)
//對列表遍歷
list.foreach(println)
//contains判斷是否包含某個元素
println(list.contains(6))
//反序,返回一個新的List
list.reverse.foreach(println)
//去前n個元素,返回一個新的List
list.take(3).foreach(println)
//刪除前n個元素,返回一個新的List
list.drop(2).foreach(println)
//判斷集合中是否有元素滿足判斷條件
println(list.exists(_ > 4))
//把List中的元素用設置的字符(串)進行拼接
list.mkString("==").foreach(print)


/*map是一個高階函數,需要一個函數參數
返回值是That,意思是誰調用的map返回的類型跟調用map方法的對象的類型一致
這裏map返回的仍然是list,因此在map中可對每一個元素進行相同操作
map返回的list的泛型由編碼傳入的函數返回類型決定,如下(_ * 100)返回的list的泛型就是Int
*/
list.map(println)
list.map(_ * 100).foreach(println)


val logList = List("Hello Scala" , "Hello Spark")
/*由上述介紹可知,split()返回一個數組,因此map返回的類型是泛型爲數組類型的list
需要對返回的list進行兩次遍歷,第一次遍歷得到Array,第二次遍歷拿到String
*/
logList.map(_.split(" ")).foreach(_.foreach(println))
/*
如果想直接拿到String,需要:
扁平操作
用到的函數是flatMap,flatMap返回的類型也是調用該方法的類型,但它可以直接得到String類型的單詞
*/
logList.flatMap(_.split(" ")).foreach(println)

  對map和flatMap的理解可參考下圖:


  Nil創建一個空List

Nil.foreach(println)
//::操作可用來添加元素
val list1 = 1::2::Nil
list1.foreach(println)

  需要注意的是, 上述創建的list均爲不可變長度的list, 即list中的元素只有在創建時才能添加. 創建可變長度的list, 需要用到ListBuffer, 看代碼:

//創建一個ListBuffer,需要導包scala.collection.mutable.ListBuffer
val listBuffer = new ListBuffer[String]
//使用+=添加元素
listBuffer.+=("hello")
listBuffer.+=("Scala")
listBuffer.foreach(println)
//使用-=去除元素
listBuffer.-=("hello")


3. set


  Scala中使用Set關鍵詞定義無重複項的集合.
  Set常用方法展示:

//創建Set集合,Scala中會自動去除重複的元素
val set1 = Set(1,1,1,2,2,3)
//遍歷Set即可使用foreach也可使用for循環
set1.foreach(x => print( x + "\t"))

val set2 = Set(1,2,3,5,7)
//求兩個集合的交集
set1.intersect(set2).foreach(println)
set1.&(set2).foreach(println)
//求差集
set2.diff(set1).foreach(println)
set2.&~(set1).foreach(println)
//求子集,如果set1中包含set2,則返回true.注意是set1包含set2返回true
println(set2.subsetOf(set1))

//求最大值
println(set1.max)
//求最小值
println(set1.min)
//轉成List類型
set1.toList.map(println)
//轉成字符串類型
set1.mkString("-").foreach(print)


4. Map


  Scala中使用Map關鍵字創建KV鍵值對格式的數據類型.

4.1 創建map集合
val map = Map(
    "1" -> "Hello",
    2 -> "Scala",
    3 -> "Spark"
)

  創建Map時, 使用->來分隔key和value, KV類型可不相同, 中間使用逗號進行分隔.

 

4.2 map遍歷


  遍歷map有三種方式, 即可使用foreach, 也可使用與Java中相同用法的迭代器, 還可使用for循環.

 

方式一: foreach


map.foreach(println)

  此時, 打印的是一個個二元組類型的數據, 關於元組我們後文中會詳細介紹, 此處只展示一下二元組的樣子: (1,Hello); (2,Scala); (3,Spark).

 

方式二: 迭代器


val keyIterator = map.keys.iterator
while (keyIterator.hasNext){
    val key = keyIterator.next()
    println(key + "--" + map.get(key).get)
}

此時需注意:
  map.get(key)返回值, 返回提示:
an option value containing the value associated with key in this map, or None if none exists.
  即返回的是一個Option類型的對象, 如果能夠獲取到值, 則返回的是一個Some(Option的子類)類型的數據, 例如打印會輸出Some(Hello), 再通過get方法就可以獲取到其值;
  如果沒有值會返回一個None(Option的子類)類型的數據, 該類型不能使用get方法獲取值(本來就無值, 強行取值當然要出異常)
  看get方法的提示(如下), 元素必須存在, 否則拋出NoSuchElementException的異常.

Returns the option's value.  Note: The option must be nonEmpty.   
Throws:
Predef.NoSuchElementException - if the option is empty.

  既然這樣, 對於None類型的數據就不能使用get了, 而是使用getOrElse(“default”)方法, 該方法會先去map集合中查找數據, 如果找不到會返回參數中設置的默認值. 例如,

//在上述map定義的情況下執行下述代碼,會在終端打印default
println(map.get(4).getOrElse("default"))
1
2


方式三: for循環


for(k <- map) println(k._1 + "--" + k._2)

  此處, 將map中的每一對KV以二元組(1, Hello)的形式賦給k這一循環變量. 可通過k._1來獲取第一個位置的值, k._2獲取第二個位置的值.

4.3 Map合併
//合併map
val map1 = Map(
  (1,"a"),    
  (2,"b"),    
  (3,"c")    
)
val map2 = Map(
  (1,"aa"),
  (2,"bb"),
  (2,90),
  (4,22),
  (4,"dd")
)
map1.++:(map2).foreach(println)

  ++和++:的區別

函數    調用    含義
++    map1.++(map2)    map1中加入map2
++:    map1.++:(map2)    map2中加入map1
  注意:map在合併時會將相同key的value替換

4.4 Map其他常見方法
//filter過濾,慮去不符合條件的記錄
map.filter(x => {
  Integer.parseInt(x._1 + "") >= 2
}).foreach(println)
//count對符合條件的記錄計數
val count = map.count(x => {
  Integer.parseInt(x._1 + "") >= 2
})
println(count);
/* 對於filter和count中條件設置使用Integer.parseInt(x._1 + "")是因爲:
 * 定義map時,第一個key使用的是String類型,但在傳入函數時每一個KV轉化爲一個二元組(Any,String)類型,x._1獲取Any類型的值,+""將Any轉化爲String,最後再獲取Int值進行判斷.
 */
//contains判斷是否包含某個key
println(map.contains(2))
//exist判斷是否包含符合條件的記錄
println(map.exists(x =>{
  x._2.equals("Scala")
}))


5. 元組

 


  元組是Scala中很特殊的一種集合, 可以創建二元組, 三元組, 四元組等等, 所有元組都是由一對小括號包裹, 元素之間使用逗號分隔.

  元組與List的區別: list創建時如果指定好泛型, 那麼list中的元素必須是這個泛型的元素; 元組創建後, 可以包含任意類型的元素.

  創建元組即可使用關鍵字Tuple, 也可直接用小括號創建, 可以加 “new” 關鍵字, 也可不加. 取值時使用 "tuple._XX"獲取元組中的值.

元組的創建和使用
//創建元組
val tuple = new Tuple1(1)
val tuple2 = Tuple2("zhangsan",2)
val tuple3 = Tuple3(1,2.0,true)
val tuple4 = (1,2,3,4)
val tuple18 = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18)
//注意:使用Tuple關鍵字最多支持22個元素
val tuple22 =  Tuple22(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)

//使用
println(tuple2._1 + "\t" + tuple2._2)
//元組中嵌套元組
val t = Tuple2((1,2),("zhangsan","lisi"))
println(t._1._2)

元組的遍歷
//tuple.productIterator可以得到迭代器, 然後用來遍歷
val tupleIterator = tuple22.productIterator
while(tupleIterator.hasNext){
    println(tupleIterator.next())
}

toString, swap方法
//toString, 將元組中的所有元素拼接成一個字符串 
println(tuple3.toString())

//swap翻轉,只對二元組有效
println(tuple2.swap)


trait特性


  Scala中的trait特性相對於Java而言就是接口. 雖然從功能上兩者極其相似, 但trait比接口還要強大許多: trait中可以定義屬性和方法的實現, 這點又有點像抽象類; Scala的類可以支持繼承多個trait, 從結果來看即實現多繼承.

  Scala中定義trait特性與類相似, 不同在於需要使用"trait"關鍵字. 其他注意點在代碼註釋中做出說明:

trait Read {
    val readType = "Read"
    val gender = "m"
    //實現trait中方法
    def read(name:String){
        println(name+" is reading")
    }
}

trait Listen {
    val listenType = "Listen"
    val gender = "m"
    //實現trait中方法
    def listen(name:String){
        println(name + " is listenning")
    }
}

//繼承trait使用extends關鍵字,多個trait之間使用with連接
class Person extends Read with Listen{
    //繼承多個trait時,如果有同名方法或屬性,必須使用“override”重新定義
    override val gender = "f"
}

object test {
    def main(args: Array[String]): Unit = {
        val person = new Person()
        person.read("zhangsan")
        person.listen("lisi")
        println(person.listenType)
        println(person.readType)
        println(person.gender)  
    }
}

object Lesson_Trait2 {
    def main(args: Array[String]): Unit = {
        val p1 = new Point(1,2)
        val p2 = new Point(1,3)
        println(p1.isEqule(p2))
        println(p1.isNotEqule(p2))
    }
}

trait Equle{
    //不實現trait中方法
    def isEqule(x:Any) :Boolean 
    //實現trait中方法
    def isNotEqule(x : Any)  = {
        !isEqule(x)
    }
}

class Point(x:Int, y:Int) extends Equle {
    val xx = x
    val yy = y
    
    def isEqule(p:Any) = {
        /*
         * isInstanceOf:判斷是否爲指定類型
         * asInstanceOf:轉換爲指定類型
         */
        p.isInstanceOf[Point] && p.asInstanceOf[Point].xx==xx
    } 
}


模式匹配match-case


  Scala中的模式匹配match-case就相當於Java中的switch-case. Scala 提供強大的模式匹配機制, 即可匹配值又可匹配類型. 一個模式匹配包含一系列備選項, 每個備選項都以case關鍵字開始. 並且每個備選項都包含了一個模式以及一到多個表達式, 箭頭符號 => 隔開了模式和表達式。

object Lesson_Match {
    def main(args: Array[String]): Unit = {
        val tuple = Tuple7(1,2,3f,4,"abc",55d,true)
        val tupleIterator = tuple.productIterator
        while(tupleIterator.hasNext){
            matchTest(tupleIterator.next())
        }
    }
/**
* 注意
* 1.模式匹配不僅可以匹配值,還可以匹配類型
* 2.模式匹配中,如果匹配到對應的類型或值,就不再繼續往下匹配
* 3.模式匹配中,都匹配不上時,會匹配到case _ ,相當於default
*/
def matchTest(x:Any) ={
    x match {
        //匹配值
        case 1 => println("result is 1")
        case 2 => println("result is 2")
        case 3 => println("result is 3")
        //匹配類型 
        case x:Int => println("type is Int")
        case x:String => println("type is String")
        case x :Double => println("type is Double")
        case _ => println("no match")
    }
    }
}

  由於匹配到對應的類型或值時, 就不再繼續往下匹配, 所有在編寫備選項時要將範圍小的放在前面, 否則就會失去意義. 這就類似於try-catch中處理異常時, 也要先從小範圍開始.

樣例類case classes
  使用case關鍵字定義的類就是樣例類(case classes), 樣例類實現類構造參數的getter方法 (構造參數默認被聲明爲val) , 當構造參數類型聲明爲var時, 樣例類會實現參數的setter和getter方法.

  樣例類默認實現toString, equals, copy和hashCode等方法. 樣例類在創建對象時可new, 也可不new.

//使用case關鍵字定義樣例類
case class Person(name:String, age:Int)

object Lesson_CaseClass {
    def main(args: Array[String]): Unit = {
        //創建樣例類對象,可new可不new
        val p1 = new Person("zhangsan",18)
        val p2 = Person("lisi",20)
        val p3 = Person("wangwu",22)
        
        val list = List(p1,p2,p3)
        list.foreach { x => {
            x match {
                case Person("zhangsan",18) => println("zhs")
                case Person("lisi",20) => println("lisi")
                case p:Person => println("is a person")
                case _ => println("no match")
            }
        } }
    }
}


併發 Actor Model


  Actor Model相當於Java中的Thread, Actor Model用來編寫並行計算或分佈式系統的高層次抽象. Actor不需要擔心多線程模式下共享鎖的問題, 可用性極高.

  Actors將狀態和行爲封裝在一個輕量級的進程/線程中, 但它不和其他Actors分享狀態, 每個Actors有自己的世界觀, 當需要和其他Actors交互時, 通過發送異步的, 非堵塞的(fire-and-forget)事件和消息來交互. 發送消息後不必等另外Actors回覆, 也不必暫停, 每個Actors有自己的消息隊列, 進來的消息按先來後到排列, 這就有很好的併發策略和可伸縮性, 可以建立性能良好的事件驅動系統.

 

Actor的特徵:

 

ActorModel是消息傳遞模型,基本特徵就是消息傳遞
消息發送是異步的,非阻塞的
消息一旦發送成功,不能修改 (類似發郵件)
Actor之間傳遞時,自己決定決定去檢查消息,而不是一直等待,是異步非阻塞的
  定義Actor需要繼承Actor這一trait, 實現act這一方法, 並且使用感嘆號! 來發送消息.

  一個簡單實例:

import scala.actors.Actor

//自定義Actor
class myActor extends Actor{
  def act(){
    while(true){
      receive {
        case x:String => println("save String ="+ x)
        case x:Int => println("save Int")
        case _ => println("save default")
      }
    }
  }
}

object Lesson_Actor {
  def main(args: Array[String]): Unit = {
    
    //創建actor的消息接收和傳遞
    val actor =new myActor()
    //啓動
    actor.start()
    //發送消息寫法
    actor ! "Hello Scala Actor !"
  }
}


  Actor與Actor之間通信:

//創建樣例類,用來發送
case class Message(actor:Actor,msg:Any)

class Actor1 extends Actor{
  def act(){
    while(true){
      //對接收的消息進行模式匹配
      receive{
        case  msg :Message => {
          println("i sava msg! = "+ msg.msg)
          //回覆消息
          msg.actor!"i love you too !"
          }
        case msg :String => println(msg)
        case  _ => println("default msg!")
      }
    }
  }
}

//爲了實現Actor中的通信,需要拿到另一個Actor的對象
class Actor2(actor :Actor) extends Actor{
  //發送消息
  actor ! Message(this,"i love you !")
    def act(){
        while(true){
            receive{
              case msg :String => {
                if(msg.equals("i love you too !")){
                  println(msg)
                 actor! "could we have a date !"
                }
              }
              case  _ => println("default msg!")
            }
        }
    }
}

object Lesson_Actor2 {
  def main(args: Array[String]): Unit = {
    val actor1 = new Actor1()
    actor1.start()
    val actor2 = new Actor2(actor1)
    actor2.start()
  }
}


Scala隱式轉換系統


  隱式轉換是指在編寫程序時, 儘量少的去編寫代碼, 讓編譯器去嘗試在編譯期間自動推導出某些信息來, 這就類似於在Scala中定義變量時不需要指定變量類型. 這種特性可以極大的減少代碼量, 提高代碼質量.

  Scala中提供強大的隱式轉換系統, 分別爲: 隱式值, 隱式視圖和隱式類.

 

隱式值


  先來看一個隱式值的Demo:

object Lesson_Implicit1 {
    def main(args: Array[String]): Unit = {
        implicit val name = "Scala Study"
        sayName
    }
    def sayName(implicit name:String) = {
        println("say love to " + name)
    }
}

  這裏將name變量聲明爲implicit, 編譯器在執行sayName方法時發現缺少一個String類型的參數, 此時會搜索作用域內類型爲String的隱式值, 並將搜索到的隱式值作爲sayName的參數值進行傳遞.

  需要注意:

隱式轉換必須滿足無歧義規則, 否則會報錯:
ambiguous implicit values: both value a of type String and value name of type String match expected type String
在同一個作用域禁止聲明兩個類型一致的變量,防止在搜索的時候會猶豫不決
聲明隱式參數的類型最好是自定義的數據類型,一般不要使用Int,String這些常用類型,防止碰巧衝突.


隱式視圖


  隱式視圖就是把一種類型自動轉換爲另一種類型. 還是先來看代碼:

object Lesson_Implicit2 {
    def main(args: Array[String]): Unit = {
        //聲明隱式視圖
        implicit def stringToInt(num:String) = Integer.parseInt(num)
        println(addNum("1000")) 
    }
     def addNum(num:Int) = {    num + 1000    }
}

  這裏addNum方法參數是String類型, 不符合定義要求, 此時編譯器搜索作用域發現有個隱式方法, 正好這個方法的參數是String, 返回是Int. 然後就會調用這個隱式方法, 返回一個Int值並將它傳給addNum方法.

隱式類


  隱式類是指把一個對象自動轉換爲另一種類型的對象, 轉換後可以調用原來不存在的方法.

package com.qb.scala

object Lesson_Implicit3 {
    def main(args: Array[String]): Unit = {
        //導入隱式類所在的包
        import com.qb.scala.Util.StringLength
        println("qwer".getLength())
    } 
}

object Util {
    //定義一個隱式類,使用implicit關鍵字修飾
    implicit class StringLength(val s : String){
        def getLength() = s.length
    }
}

  這裏編譯器在qwer對象調用getLength方法時, 發現該對象並沒有getLength方法, 此時編譯器發現在作用域範圍內有隱式實體. 發現有符合的隱式類可以用來轉換成帶有getLength方法的Util類, 進而就可調用getLength方法.

  需要注意:

隱式類所帶的構造參數有且只能有一個
必須在類, 伴生對象和包對象中定義隱式類
隱式類不能是case class(樣例類), case class 在定義時會自動生成伴生對象.
作用域中不能有與隱式類同名的標識符

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章