Scala 資料結構
1.資料結構
Scala同時支援可變集合和不可變集合,不可變集合從不可變,可以安全的併發訪問,兩個主要的包:
不可變集合:scala.collection.immutable
可變集合: scala.collection.mutable
Scala優先採用不可變集合,對於幾乎所有的集合類,Scala都同時提供了可變和不可變的版本。
不可變集合繼承層次:
可變集合繼承層次:
2.陣列 Array
(1)定長陣列(宣告泛型)
//定義 val arr1 = new Array[Int](10) //賦值 arr1(1) = 7 // 集合元素採用小括號訪問 或: //定義 val arr1 = Array(1, 2)
(2)變長陣列(宣告泛型)
//定義
val arr2 = ArrayBuffer[Int]()
//追加值
arr2.append(7)
//重新賦值
arr2(0) = 7
(3)定長陣列與變長陣列的轉換
arr1.toBuffer
arr2.toArray
(4)多維陣列
//定義
val arr3 = Array.ofDim[Double](3,4)
//賦值
arr3(1)(1) = 11.11
(5)與Java陣列的互轉
Scal陣列轉Java陣列: def main(args: Array[String]): Unit = { import scala.collection.mutable.ArrayBuffer val arr4 = ArrayBuffer("1", "2", "3") //Scala to Java import scala.collection.JavaConversions.bufferAsJavaList val javaArr = new ProcessBuilder(arr4) println(javaArr.command()) }
Java陣列轉Scala陣列:
import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.mutable.Buffer
val scalaArr: Buffer[String] = javaArr.command()
println(scalaArr)
(6)陣列的遍歷
for(x <- arr1) {
println(x)
}
3.元組 Tuple
元組也是可以理解為一個容器,可以存放各種相同或不同型別的資料。
(1)元組的建立
val tuple1 = (1, 2, 3, "heiheihei") println(tuple1)
(2)元組資料的訪問,注意元組元素的訪問有下劃線,並且訪問下標從1開始,而不是0
val value1 = tuple1._4
println(value1)
(3)元組的遍歷
def main(args: Array[String]): Unit = {
val tuple1 = (1, 2, 3, "heiheihei")
println(tuple1)
println("===")
for (elem <- tuple1.productIterator) {
println(elem)
}
println("===")
tuple1.productIterator.foreach(println(_))
}
4.列表 List(補:預設不可變,和陣列一樣)
如果希望得到一個空列表,可以使用Nil物件。
(1)建立List
val list1 = List(1, 2)
println(list1)
(2)訪問List元素
val value1 = list1(1)
println(value1)
(3) List元素的追加
val list2 = list1 :+ 99
println(list2)
val list3 = 100 +: list1
println(list3)
(4)List的建立與追加,符號“::”,注意觀察去掉Nil和不去掉Nil的區別
val list4 = 1 :: 2 :: 3 :: list1 :: Nil
println(list4)
注意:使用兩個冒號可以增加元素,如果使用三個冒號可以將集合中的元素增加的新的列表中
5.佇列 Queue
佇列資料存取符合先進先出策略
(1)佇列的建立
import scala.collection.mutable
val q1 = new mutable.Queue[Int]
println(q1)
(2)佇列元素的追加
q1 += 1;
println(q1)
(3)向佇列中追加List
q1 ++= List(2, 3, 4)
println(q1)
(4)按照進入佇列的順序刪除元素
q1.dequeue()
println(q1)
(5)塞入資料
q1.enqueue(9, 8, 7)
println(q1)
(6)返回佇列的第一個元素
println(q1.head)
(7)返回佇列最後一個元素
println(q1.last)
(8)返回除了第一個以外剩餘的元素
println(q1.tail)
6.對映 Map
這個地方的學習,就類比Java的map集合學習即可。
(1)構造不可變對映
val map1 = Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> 30)
(2) 構造可變對映
val map2 = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> 30)
(3)空的對映
val map3 = new scala.collection.mutable.HashMap[String, Int]
(4) 對偶元組
val map4 = Map(("Alice", 10), ("Bob", 20), ("Kotlin", 30))
(5) 取值
如果對映中沒有值,則會丟擲異常,使用contains方法檢查是否存在key。如果通過 對映.get(鍵) 這樣的呼叫返回一個Option物件,要麼是Some,要麼是None。
val value1 = map1("Alice")//建議使用get方法得到map中的元素
println(value1)
(6)更新值
map2("Alice") = 99
println(map2("Alice"))
或:
map2 += ("Bob" -> 99)
map2 -= ("Alice", "Kotlin")
println(map2)
或:
val map5 = map2 + ("AAA" -> 10, "BBB" -> 20)
println(map5)
(7)遍歷
for ((k, v) <- map1) println(k + " is mapped to " + v)
for (v <- map1.keys) println(v)
for (v <- map1.values) println(v)
for(v <- map1) prinln(v)
7.集 Set
集是不重複元素的結合。集不保留順序,預設是以雜湊集實現。
如果想要按照已排序的順序來訪問集中的元素,可以使用SortedSet(已排序資料集),已排序的資料集是用紅黑樹實現的。
預設情況下,Scala 使用的是不可變集合,如果你想使用可變集合,需要引用scala.collection.mutable.Set 包。
(1)Set不可變集合的建立
val set = Set(1, 2, 3)
println(set)
(2)Set可變集合的建立,如果import了可變集合,那麼後續使用預設也是可變集合
import scala.collection.mutable.Set
val mutableSet = Set(1, 2, 3)
(3)可變集合的元素新增
mutableSet.add(4)
mutableSet += 6
// 注意該方法返回一個新的Set集合,而非在原有的基礎上進行新增
mutableSet.+(5)
(4) 可變集合的元素刪除
mutableSet -= 1
mutableSet.remove(2)
println(mutableSet)
(5) 遍歷
for(x <- mutableSet) {
println(x)
}
8.集合元素與函式的對映
(1)map:將集合中的每一個元素通過指定功能(函式)對映(轉換)成新的結果集合
val names = List("Alice","Bob","Nick")
//集合中每一個元素轉換為大寫
println(names.map(_.toUpperCase))
println(names.map(x=>x.toUpperCase()))
println(names.map(x=>{
x.toUpperCase()
}))
//集合中每一個元素轉換為小寫
println(names.map(_.toLowerCase))
println(names.map(x=>x.toLowerCase()))
println(names.map(x=>{
x.toLowerCase()
}))
(2)flatmap:flat即壓扁,壓平,扁平化,效果就是將集合中的每個元素的子元素對映到某個函式並返回新的集合
val names = List("Alice", "Bob", "Nick")
println(names.flatMap(_.toUpperCase()))
9.摺疊、化簡、掃描
(1) 摺疊,化簡:將二元函式引用於集合中的函式
println("===化簡、摺疊、掃描===")
val list=List(1,2,3)
//(((1 - 2) - 3) - 4) - 5)
val i1 = list.reduceLeft((x,y)=>x-y)
val i2 = list.reduceLeft(_-_)
// (1 - (2 - (3 - (4 - 5))))
val i3 = list.reduceRight((x,y)=>x-y)
val i4 = list.reduceRight(_-_)
println(i1)
println(i2)
println(i3)
println(i4)
(2) 摺疊,化簡:fold
fold函式將上一步返回的值作為函式的第一個引數繼續傳遞參與運算,直到list中的所有元素被遍歷。可以把reduceLeft看做簡化版的foldLeft。相關函式:fold,foldLeft,foldRight,可以參考reduce的相關方法理解。
//函式的顆粒化
val list2 = List(1, 9, 2, 8)
val i4 = list2.fold(5)((sum, y) => sum + y) //初始值是5
println(i4)
foldRight
val list3 = List(1, 9, 2, 8)
val i5 = list3.foldRight(100)(_ - _)
println(i5)
尖叫提示:foldLeft和foldRight有一種縮寫方法對應分別是:/:和:\
例如:
foldLeft
val list4 = List(1, 9, 2, 8)
val i6 = (0 /: list4)((res, next) =>res - next)
println(i6)
(3) 統計一句話中,各個文字出現的次數
println("===統計一句話中,各個文字出現的次數===")
val sentence = "一首現luomk 哈哈代詩《笑裡藏刀》:哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈刀哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈"
val i7 = (Map[Char, Int]() /: sentence)((m, c) => m + (c -> (m.getOrElse(c, 0) + 1)))
println(i7)
m相當於Map[Char, Int](),c相當於Char, 相當於往map中加元素,但map新增元素,key相同就替換,所有需要+1(m.getOrElse(c, 0)表示在m中取c,沒有則為0,有則獲得這個值 )
(4) 摺疊,化簡,掃描
這個理解需要結合上面的知識點,掃描,即對某個集合的所有元素做fold操作,但是會把產生的所有中間結果放置於一個集合中儲存。
//這個理解需要結合上面的知識點,掃描,即對某個集合的所有元素做fold操作,但是會把產生的所有中間結果放置於一個集合中儲存。
println("=== 摺疊,化簡,掃描===")
val i8 = (1 to 10).scanLeft(0)(_ + _)
val i9 = (1 to 10).foldLeft(0)(_ + _)
println(i8)
println(i9)
10.拉鍊
val list1 = List("15837312345", "13737312345", "13811332299")
val list2 = List(17, 87)
val i1 = list1 zip list2
println(i1) // List((15837312345,17), (13737312345,87))
print(i1(0)._2) //17
11. 迭代器
val iterator = List(1, 2, 3, 4, 5).iterator
while (iterator.hasNext) {
println(iterator.next())
}
或:
for(enum <- iterator) {
println(enum)
}
你可以通過iterator方法從集合獲得一個迭代器,通過while迴圈和for表示式對集合進行遍歷。
12.流 Stream
stream是一個集合。這個集合,可以用於存放,無窮多個元素,但是這無窮個元素並不會一次性生產出來,而是需要用到多大的區間,就會動態的生產,末尾元素遵循lazy規則。
(1)使用#::得到一個stream
def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)
(2)傳遞一個值,並列印stream集合
val tenOrMore = numsForm(10)
println(tenOrMore)
(3)tail的每一次使用,都會動態的向stream集合按照規則生成新的元素
println(tenOrMore.tail)
println(tenOrMore)
(4) 使用map對映stream的元素並進行一些計算
println(numsForm(5).map(x => x * x))
13.檢視 View
Stream的懶執行行為,你可以對其他集合應用view方法來得到類似的效果,該方法產出一個其方法總是被懶執行的集合。但是view不會快取資料,每次都要重新計算。
例如:我們找到10萬以內,所有數字倒序排列還是它本身的數字。
def main(args: Array[String]): Unit = {
//例如:我們找到10萬以內,所有數字倒序排列還是它本身的數字。
val viewSquares = (1 to 100000).view.map(x => x).filter(x => {x.toString == x.toString.reverse})
for (x <- viewSquares) {
print(x + ",")
}
}
14.執行緒安全的集合
所有執行緒安全的集合都是以Synchronized開頭的集合,例如:
SynchronizedBuffer
SynchronizedMap
SynchronizedPriorityQueue
SynchronizedQueue
SynchronizedSet
SynchronizedStack
15.並行集合
Scala為了充分使用多核CPU,提供了並行集合(有別於前面的序列集合),用於多核環境的平行計算。
主要用到的演算法有:
Divide and conquer : 分治演算法,Scala通過splitters,combiners等抽象層來實現,主要原理是將計算工作分解很多工,分發給一些處理器去完成,並將它們處理結果合併返回
Work stealin:演算法,主要用於任務排程負載均衡(load-balancing),通俗點完成自己的所有任務之後,發現其他人還有活沒幹完,主動(或被安排)幫他人一起幹,這樣達到儘早幹完的目的。
(1) 列印1~5
(1 to 5).foreach(println(_))
println()
(1 to 5).par.foreach(println(_))
(2) 檢視並行集合中元素訪問的執行緒
val result1 = (0 to 10000).map{case _ => Thread.currentThread.getName}.distinct
val result2 = (0 to 10000).par.map{case _ => Thread.currentThread.getName}.distinct
println(result1)
println(result2)
16.操作符
這部分內容沒有必要刻意去理解和記憶,語法使用的多了,自然就會產生感覺,該部分內容暫時大致瞭解一下即可。
(1) 如果想在變數名、類名等定義中使用語法關鍵字(保留字),可以配合反引號反引號:
val `val` = 42
(2) 這種形式叫中置操作符,A操作符B等同於A.操作符(B)
(3) 後置操作符,A操作符等同於A.操作符,如果操作符定義的時候不帶()則呼叫時不能加括號
(4) 前置操作符,+、-、!、~等操作符A等同於A.unary_操作符
(5) 賦值操作符,A操作符=B等同於A=A操作符B