1. 程式人生 > >大資料(十一)--Scala程式語言-提高

大資料(十一)--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"))
  • 方式三: 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 =