天天看點

Scala入門系列(9)-Scala之集合集合集合操作

集合

Scala同時支援不可變集合和可變集合,不可變集合可以安全的并發通路。Scala預設采用不可變集合,對于幾乎所有的集合類,Scala都同時提供了 可變(mutable)和不可變(immutable)的版本。Scala的集合有三大類:序列Seq、集Set、映射Map,所有的集合都擴 展自Iterable特質,在Scala中集合有可變(mutable)和不可變 (immutable)兩種類型

  • 不可變集合:scala.collection.immutable
  • 可變集合: scala.collection.mutable

不可變集合

scala不可變集合,就 是這個集合本身不能動态變化。(類 似java的數組,是不可以動态增長的)。

繼承關系圖:

Scala入門系列(9)-Scala之集合集合集合操作
  1. Set、Map是Java中也有的集合
  2. Seq是Java沒有的,我們發現List歸屬 到Seq了,是以這裡的List就和java不是同 一個概念了
  3. 我們前面的for循環有一個 1 to 3 , 就是IndexedSeq 下的Vector
  4. String也是屬于IndexeSeq
  5. 我們發現經典的資料結構比如 Queue 和 Stack被歸屬到LinearSeq
  6. 大家注意Scala中的Map體系有一個 SortedMap,說明Scala的Map可以支援排
  7. IndexSeq 和 LinearSeq 的差別 [IndexSeq是通過索引來查找和定位, 是以速度快,比如String就是一個索引 集合,通過索引即可定位] [LineaSeq 是線型的,即有頭尾的概念, 這種資料結構一般是通過周遊來查找, 它的價值在于應用到一些

可變集合

就是這個集合 本身可以動态變化的。(比 如:ArrayList , 是可以動态增長的)。

繼承關系圖:

Scala入門系列(9)-Scala之集合集合集合操作

數組

定長數組

object ArrayTest {
  def main(args: Array[String]): Unit = {
    // 第一種方式定,等同于Java中的數組,中括号的類型就是數組的類型,小括号設定數組的長度
    val array = new Array[Int](5)
    // 長度
    println(array.length)
    // 指派,集合元素采用小括号通路
    array(0) = 2
    // 循環
    for (item <- array) {
      println(item)
    }
    // 第二種方式,在定義數組時,直接指派
    val array002 = Array(1, 2, 3, 4, 5, 6)
    for (i <- 0 to array002.length - 1) {
      println(array002(i))
    }
  }
}
           

變長數組

ArrayBuffer是變長數組,類似java的ArrayList

object ArrayTest002 {
  def main(args: Array[String]): Unit = {
    // 定義一個數組
    val array001 = ArrayBuffer[Int]()
    // 添加一個元素 ,每append一次,arr在底層會重新配置設定空間,進行擴容,arr2的記憶體位址會發 生變化,也就成為新的ArrayBuffer
    array001.append(9090, 0, 7878)
    // 重新指派
    array001(0) = 1010
    // 删除
    array001.remove(0)
  }
}
           

數組轉換

val array = array001.toArray
    val arrayBuffer = array.toBuffer
           

多元數組

多元數組一個數組中的值可以是另一個數組,另一個數組的值也可以是一個數組。矩陣與表格是我們常見的二維數組。

// 包含三個數組元素,每個數組元素又含有三個值。
    val myMatrix = Array.ofDim[Int](3, 3)
           

元組Tuple

元組也是可以了解為一個容器,可以存放各種相同或不同類型的資料。 說的簡單點,就是将多個無關的資料封裝為一個整體,稱為元組, 最多的特點靈 活,對資料沒有過多的限制。元組中最大隻能有22個元素。

object TupleTest {
  def main(args: Array[String]): Unit = {
    // 建立
    val tuple = (1, 2, 2, "Hello")
    println(tuple)
    // 通路元組中的資料,可以采用順序号(_順序号),也可以通過索引 (productElement)通路。
    println(tuple._1)
    println(tuple.productElement(1))

    // Tuple是一個整體,周遊需要調其疊代器。
    for (item <- tuple.productIterator) {
      println(item)
    }
  }
}
           

List

Scala中的List 和Java List 不一樣,在Java中List是一個接口,真正存放資料是 ArrayList,而Scala的List可以直接存放資料,就是一個object,預設情況下 Scala的List是不可變的,List屬于序列Seq。

object ListTest {
  def main(args: Array[String]): Unit = {
    val list01 = List(1, 2, "555") //建立時,直接配置設定元素
    println(list01)
    val list02 = Nil //空集合
    println(list02)

    // 通路List元素,和數組通路方式一緻
    val item = list01(0)
    println(item)

    // 添加元素:向清單中增加元素, 會傳回新的清單/集合對象。
    // 注意:Scala中List元素的追加 形式非常獨特,和Java不一樣。

    // :+運算符表示在清單的最後增加資料,原先的集合沒有變化,
    val list003 = list01 :+ 4
    // 符号::表示向集合中 建立集合添加元素。運算時,集合對象一定要放置在最右邊,
    // 運算規則,從右向左。::: 運算符是将集合中的每一個元素加入到空集合中去
    val list = List(1, 2, 3)
    val listTest = 4 :: 5 :: list :: 6 :: Nil
    println("listTest:" + listTest)
    println(list003)
    // +:運算符表示在清單的最前增加資料
    val list004 = 232 +: list01
    println(list004)

  }
}
           

注意事項:

  1. List預設為不可變的集合
  2. List 在 scala包對象聲明的,是以不需要引入其它包也可以使用
  3. val List = scala.collection.immutable.List
  4. List 中可以放任何資料類型,比如 arr1的類型為 List[Any]
  5. 如果希望得到一個空清單,可以使用Nil對象, 在 scala包對象聲明的,是以不需 要引入其它包也可以使用

ListBuffer

ListBuffer是可變的list集合,可以添加,删除元素,ListBuffer屬于序列。

object ListBufferTest {
  def main(args: Array[String]): Unit = {
    // 建立ListBuffer
    val listTest001 = ListBuffer[String]() // 空集合
    val listTest002 = ListBuffer[String]("hello", "world") //帶元素的集合

    // 通路元素
    println(listTest002(0))

    // 添加元素
    listTest002 += "張三"
    listTest002.append("李四", "王五")

    // 把一個集合添加到另一個集合中
    listTest001 ++= listTest002
    println(listTest001)
    val list003 = listTest001 ++ listTest002
    println(list003)

    // 删除元素
    listTest002.remove(0)
  }
}
           

Queue

隊列是一個有序清單,在底層可以用數組或是連結清單來實作。其輸入和輸出要遵循先入先出的原則。即:先存入隊列的資料,要先取出。 後存入的要後取出。在Scala中,由設計者直接給我們提供隊列類型使用。有scala.collection.mutable.Queue scala.collection.immutable.Queue , 一般來說,我們在開發中通常使用可變集合 中的隊列。

object QueueTest {
  def main(args: Array[String]): Unit = {
    //
    import scala.collection.mutable.Queue
    // 這裡的Int是泛型,希望可以存放其它類型,則使用Any即可。
    val queueTest = new Queue[Int]

    // 向隊列中追加單個元素
    queueTest += 20
    println(queueTest)
    // 追加List
    queueTest ++= List(4, 5, 6)
    println(queueTest)
    //隊列位,按照隊列的算法,會将資料添加到隊列的最後
    queueTest.enqueue(7, 8)
    println(queueTest)

    //删除元素 按照進入隊列的順序删除元素(隊列先進先出)
    queueTest.dequeue() //隊列頭
    println(queueTest)

    // 隊列 Queue-傳回隊列的元素
    println(queueTest.head) //傳回隊列的第一個元素
    println(queueTest.last) //傳回隊列最後一個元素
    println(queueTest.tail) //傳回隊列的尾部,傳回除了第一個以外剩餘的元素, 可以級聯使用,這個在遞歸時使用較多。
    println(queueTest.tail.tail)
  }
}
           

Map

在Java中,HashMap 是一個散清單(數組+連結清單),它存儲的内容是鍵值對(key-value)映射, Java中的HashMap是無序的,key不能重複。Scala中的Map 和Java類似,也是一個散清單,它存儲的内容也是鍵值對 (key-value)映射,Scala中不可變的Map是有序的,可變的Map是無序的。有可變Map (scala.collection.mutable.Map) 和 不可變 Map(scala.collection.immutable.Map)

object MapTest {
  def main(args: Array[String]): Unit = {
    // 構造不可變映射
    // Scala中的不可變Map是有序,建構Map中的元素底層是Tuple2類型。
    val map1 = Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> "北京")
    println(map1)
    // 構造可變映射 需要指定可變Map的包
    val map2 = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> 30)
    println(map2) // 輸出順序和聲明順序不一緻
    // 建立空的映射
    val map3 = new scala.collection.mutable.HashMap[String, Int]
    println(map3)
    // 對偶元組,是隻含有兩個資料的元組。
    val map4 = mutable.Map(("A", 1), ("B", 2), ("C", 3), ("D", 30))
    println("map4=" + map4)

    //擷取元素
    println(map1("Alice")) // 使用map(key),如果key不存在,則抛出異常[java.util.NoSuchElementException],在Java中,如果key不存在則傳回null
    // 判斷是否存在某個KEY
    println(map1.contains("aaa"))
    // 使用map.get(key).get取值,key存在傳回some,如果key不存在,則傳回None
    println(map1.get("Alice"))
    println(map1.get("aaa"))
    println(map1.get("Alice").get)
    // 使用map4.getOrElse()取值,如果key存在,傳回key對應的值。不存在,傳回預設值
    println(map1.getOrElse("aaa", "預設值"))

    // 添加元素
    map2 += ("lisan" -> 10)
    map2 += ("lisan" -> 20, "wangwu" -> 50)
    println(map2)

    // 删除元素
    map2 -= ("lisan", "aaa")
    println(map2)

    // 周遊
    for ((k, v) <- map2) {
      println(k + ":" + v)
    }
    for (k <- map2.keys) {
      println(k + ":" + map2.get(k).get)
    }
  }
}
           

Set

集是不重複元素的結合。集不保留順序,預設是以哈希集實作。

java中,HashSet是實作Set接口的一個實體類,資料是以哈希表的形式存放 的,裡面的不能包含重複資料。Set接口是一種不包含重複元素的 collection, HashSet中的資料也是沒有順序的。

預設情況下,Scala 使用的是不可變集合,如果你想使用可變集合,需要引用 scala.collection.mutable.Set 包。

object SetTest {
  def main(args: Array[String]): Unit = {
    // 不可變集合
    val setTest = Set(1, 2, 3)
    println(setTest)

    // 可變集合
    import scala.collection.mutable.Set
    val mutableSet = Set(1, 2, 3)
    println(mutableSet)

    // 元素添加,如果添加的對象已經存在,則不會重複添加,也不會報錯
    mutableSet.add(4)
    mutableSet += 5
    mutableSet.+=(7, 8, 9)
    println(mutableSet)

    // 元素删除,如果删除的對象不存在,則不生效,也不會報錯
    mutableSet.remove(1)
    mutableSet -= 2
    println(mutableSet)

    // 周遊
    for (v <- mutableSet) {
      println(v)
    }
  }
}

           

集合操作

map映射

将集合中的每一個元素通過指定功能 (函數)映射(轉換)成新的結果集合這裡其實就是所謂的将函數作為參數傳遞給另外一個函數,這是函數式程式設計的特點。

object CollectionTest {
  def main(args: Array[String]): Unit = {
    // 将 val names = List("Alice", "Bob", "Nick") 中的所有單詞,全部轉成字母大寫, 傳回到新的List集合中
    def upper(name: String): String = {
      name.toUpperCase
    }

    val names = List("Alice", "Bob", "Nick")
    val strings: List[String] = names.map(upper)
    println(strings)
  }
}
           

flatmap映射

flat即壓扁,壓平,扁平化,效果就是将集合中的每個元素的子元素映 射到某個函數并傳回新的集合。

val value: Any = names.flatMap(upper)
    println(value)
           

filter

将符合要求的資料(篩選)放置到新的集合中。

// 将 val names = List("Alice", "Bob", "Nick") 集合中首字母為'A'的 篩選到新的集合。
    def startWithA(name: String): Boolean = {
      name.startsWith("A")
    }

    val strings1: List[String] = names.filter(startWithA)
    println(strings1)
           

化簡

将二進制函數引用于集合中的函數。

//val list = List(1, 20, 30, 4 ,5) , 求出list的和
    val list = List(1, 20, 30, 4, 5)
    def sum(n1: Int, n2: Int): Int = {
      n1 + n2
    }
    val res = list.reduceLeft(sum)
    println("res=" + res)
           

折疊

fold函數将上一步傳回的值作為函數的第一個參數繼續傳遞參與運算,直到list 中的所有元素被周遊。

val list = List(1, 2, 3, 4)
    def minus(num1: Int, num2: Int): Int = {
      num1 - num2
    }
    println(list.foldLeft(5)(minus)) // 函數的柯裡化
    println(list.foldRight(5)(minus)) 
           

掃描

即對某個集合的所有元素做fold操作,但是會把産生的所有中間結果放置 于一個集合中儲存。

在這裡插入代碼片
           

拉鍊(合并)

當我們需要将兩個集合進行 對偶元組合并,可以使用拉鍊。

val list1 = List(1, 2, 3)
    val list2 = List(4, 5, 6)
    val list3 = list1.zip(list2)
    println(list3)
           

疊代器

通過iterator方法從集合獲得一個疊代器,通過while循環和for表達式對集合進行 周遊.(學習使用疊代器來周遊)。

val list1 = List(1, 2, 3)
    val iterator: Iterator[Int] = list1.iterator
    while (iterator.hasNext) {
      println(iterator.next())
    }
           

流 Stream

stream是一個集合。這個集合,可以用于存放無窮多個元素,但是這無窮個元 素并不會一次性生産出來,而是需要用到多大的區間,就會動态的生産,末尾元 素遵循lazy規則(即:要使用結果才進行計算的) 。

object StreamTest {
  def main(args: Array[String]): Unit = {
    // 格式:numsForm(參數:參數類型) : 傳回值類型 = 參數 #:: numsForm(m+1)
    def numsForm(n: BigInt): Stream[BigInt] = n #:: numsForm(n + 1)
    val stream1 = numsForm(1)
  }
}
           

注意事項:

  1. Stream 集合存放的資料類型是BigInt
  2. numsForm 是自定義的一個函數,函數名是程式員指定的。
  3. 建立的集合的第一個元素是 n , 後續元素生成的規則是 n + 1
  4. 後續元素生成的規則是可以程式員指定的 ,比如 numsForm( n * 4)…

視圖 View

Stream的懶加載特性,也可以對其他集合應用view方法來得到類似的效果。

注意事項:

  1. view方法産出一個總是被懶執行的集合。
  2. view不會緩存資料,每次都要重新計算, 比如周遊View時。

線程安全的集合

所有線程安全的集合都是以Synchronized開頭的集合。

并行集合

Scala為了充分使用多核CPU。主要用到的算法有: Divide and conquer : 分治算法,Scala通過splitters(分解器),combiners (組合器)等抽象層來實作,主要原理是将計算工作分解很多任務,分發給 一些處理器去完成,并将它們處理結果合并傳回。

操作符

  1. 如果想在變量名、類名等定義中使用文法關鍵字(保留字),可以配合反引号反引号
  2. 置操作符:A 操作符 B 等同于 A.操作符(B)
  3. 後置操作符:A操作符 等同于 A.操作符,如果操作符定義的時候不帶()則調用 時不能加括号
  4. 前置操作符,+、-、!、~等操作符A等同于A.unary_操作符
  5. 指派操作符,A 操作符= B 等同于 A = A 操作符 B ,比如 A += B 等價 A = A + B

繼續閱讀