Scala: 集合

2020-08-14 11:06:38

1 集合 COLLECTIONS

1.1 Scala 集合

在这里插入图片描述
Map 類和 Set 類都有可變和不可變兩種型別
Map and Set come in both mutable and immutable versions.

一些運算子號的理解:
向集閤中新增單個元素使用 +, 將集閤中的元素加入另一個集合需要用 ++

+ 或者 ++ 後可以使用 : 或 = , 靠近 一側的表示變數,List 是一個鏈表,新增元素最好使用前置方式新增元素: item +: List

=> 符號代表的意思將左邊的變數傳給右邊的計算方法進行計算

item <- items 表示將 items 中的每個元素依次賦值給 item

對集合使用的理解

  • ArrayBuffer 是可變型別集合,新增刪除元素較爲頻繁的可以用這個集合
  • List 是一個不可變的鏈表,新增元素使用前置方式新增
  • Vector 不可量的集合,在該集合上可以使用索引,其他與 List 類似
  • Set 裏面不可以有重複的值

1.2 ArrayBuffer

ArrayBuffer是一個可變的類,可以通過類提供的方法修改類中元素

// 匯入類包
import scala.collection.mutable.ArrayBuffer

// 建立 ArrayBudder 型別變數
val ints = ArrayBuffer[Int]()
val names = ArrayBuffer[String]()

// 向 ArrayBudder 變數中新增元素
ints += 1
ints += 2

初始化元素的方式建立 ArrayBudder 類變數

val nums = ArrayBuffer(1, 2, 3)

// add one element
nums += 4

// add multiple elements
nums += 5 += 6

// add multiple elements from another collection
nums ++= List(7, 8, 9)

可以使用 -= 和 --= 方法從 ArrayBuffer 中移除元素

// remove one element
nums -= 9

// remove multiple elements
nums -= 7 -= 8

// remove multiple elements using another collection
nums --= Array(5, 6)

ArrayBuffer 中更多方法

val a = ArrayBuffer(1, 2, 3)         // ArrayBuffer(1, 2, 3)
a.append(4)                          // ArrayBuffer(1, 2, 3, 4)
a.append(5, 6)                       // ArrayBuffer(1, 2, 3, 4, 5, 6)
a.appendAll(Seq(7,8))                // ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8)
a.clear                              // ArrayBuffer()

val a = ArrayBuffer(9, 10)           // ArrayBuffer(9, 10)
a.insert(0, 8)                       // ArrayBuffer(8, 9, 10)
a.insertAll(0, Vector(4, 5, 6, 7))   // ArrayBuffer(4, 5, 6, 7, 8, 9, 10)
a.prepend(3)                         // ArrayBuffer(3, 4, 5, 6, 7, 8, 9, 10)
a.prepend(1, 2)                      // ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
a.prependAll(Array(0))               // ArrayBuffer(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

val a = ArrayBuffer.range('a', 'h')  // ArrayBuffer(a, b, c, d, e, f, g)
a.remove(0)                          // ArrayBuffer(b, c, d, e, f, g)
a.remove(2, 3)                       // ArrayBuffer(b, c, g)

val a = ArrayBuffer.range('a', 'h')  // ArrayBuffer(a, b, c, d, e, f, g)
a.trimStart(2)                       // ArrayBuffer(c, d, e, f, g)
a.trimEnd(2)                         // ArrayBuffer(c, d, e)

1.3 List

List 爲不可變鏈表,這意味着你新增或刪除裏面的元素,都會新建一個 List.

// 建立 List
val ints = List(1, 2, 3)
val names = List("Joel", "Chris", "Ed")

// 也可以宣告變數型別
val ints: List[Int] = List(1, 2, 3)
val names: List[String] = List("Joel", "Chris", "Ed")

在 List 中新增刪除元素都會新建一個 List

val a = List(1,2,3)

// 前置新增元素  prepend elements to a List
val b = 0 +: a
// 將另一個List 前置
val b = List(-1, 0) ++: a

效果如下所示:

scala> val b = 0 +: a
b: List[Int] = List(0, 1, 2, 3)

scala> val b = List(-1, 0) ++: a
b: List[Int] = List(-1, 0, 1, 2, 3)

List 是一個單鏈表, 通過這種前置新增的方式能使新增速度更快;在中間新增或尾部新增都會有遍歷元素的動作,拖慢速度

如何理解類的操作方法
操作:
**:**與集合在同一側,例如:

val a = List(1,2,3)
// 集合需在右側
0 +: a
// 集合需在左側
a :+ 4

Seq and Vector. 中也可以用 : 方法

List 遍歷

val names = List("Joel", "Chris", "Ed")

for (name <- names) println(name)

這種遍歷方式在集閤中是通用的,如 ArrayBuffer, List, Seq, Vector

1.4 Vector

Vector 是一個具有索引特性,不可變的序列。索引意味着可以在 O(1) 的時間複雜度內通過索引找到元素。

Vector 與 List 的主要區別就是 Vector 有索引,而 List 沒有。 其它用法相似

val nums = Vector(1, 2, 3, 4, 5)

val strings = Vector("one", "two")

val peeps = Vector(
    Person("Bert"),
    Person("Ernie"),
    Person("Grover")
)
val a = Vector(1,2,3)
// append elements,會新建一個 Vector
val b = a :+ 4
val b = a ++ Vector(4, 5)

 // prepend elements 
scala> val b = 0 +: a
b: Vector[Int] = List(0, 1, 2, 3)

scala> val b = Vector(-1, 0) ++: a
b: Vector[Int] = List(-1, 0, 1, 2, 3)

兩種新增元素的方式速度大致相同

// 遍歷元素
scala> val names = Vector("Joel", "Chris", "Ed")
val names: Vector[String] = Vector(Joel, Chris, Ed)

scala> for (name <- names) println(name)
Joel
Chris
Ed

1.5 Map

Map 類是一個可迭代序列,裏面包含鍵值對
The Map class documentation describes a Map as an iterable sequence that consists of pairs of keys and values.

val states = Map(
    "AK" -> "Alaska",
    "IL" -> "Illinois",
    "KY" -> "Kentucky"
)

在 Scala 中, Map 有可變和不可變兩種型別

下面 下麪以 可變型別爲例進行介紹

// 匯入可變 map 包
import scala.collection.mutable.Map

val states = collection.mutable.Map("AK" -> "Alaska")
// 新增元素
states += ("AL" -> "Alabama")
states += ("AR" -> "Arkansas", "AZ" -> "Arizona")
// 從其它 Map 中新增元素
states ++= Map("CA" -> "California", "CO" -> "Colorado")

// 移除元素
states -= "AR"
states -= ("AL", "AZ")
states --= List("AL", "AZ")

// 更新值
scala> states("AK") = "Alaska, A Really Big State"
scala> states
res6: scala.collection.mutable.Map[String,String] = Map(CO -> Colorado, CA -> California, AK -> Alaska, A Really Big State)

有多種方式遍歷 Map

val ratings = Map(
    "Lady in the Water"-> 3.0, 
    "Snakes on a Plane"-> 4.0,
    "You, Me and Dupree"-> 3.5
)

// for 語句
for ((k,v) <- ratings) println(s"key: $k, value: $v")

// 使用 forwach 方法和 match 表達式
ratings.foreach {
    case(movie, rating) => println(s"key: $movie, value: $rating")
}

Map 中的方法
在这里插入图片描述
不可變 map 型別中的方法,會建立新的 Map
在这里插入图片描述
可變 map 型別中的方法
在这里插入图片描述

1.6 Set

Scala 是一個可迭代集合,集閤中沒有相同元素
The Scala Set class is an iterable collection with no duplicate elements.

Scala 中 Set 也有可變和不可變兩種。

下面 下麪以可變類爲例進行介紹

val set = scala.collection.mutable.Set[Int]()
// 向集閤中新增元素
set += 1
set += 2 += 3
set ++= Vector(4, 5)

向 Set 中加入已有的元素會被忽略

通過 add 方法新增元素會返回 Boolean 值, 新增成功返回 true, 失敗返回 false, 新增已有的元素會返回 false

scala> set.add(6)
res4: Boolean = true

scala> set.add(5)
res5: Boolean = false

刪除元素

scala> val set = scala.collection.mutable.Set(1, 2, 3, 4, 5)
set: scala.collection.mutable.Set[Int] = Set(2, 1, 4, 3, 5)

// one element
scala> set -= 1
res0: scala.collection.mutable.Set[Int] = Set(2, 4, 3, 5)

// two or more elements (-= has a varargs field)
scala> set -= (2, 3)
res1: scala.collection.mutable.Set[Int] = Set(4, 5)

// multiple elements defined in another sequence
scala> set --= Array(4,5)
res2: scala.collection.mutable.Set[Int] = Set()

Set 中海油 clear 和 remove 方法

scala> val set = scala.collection.mutable.Set(1, 2, 3, 4, 5)
set: scala.collection.mutable.Set[Int] = Set(2, 1, 4, 3, 5)

// clear
scala> set.clear()

scala> set
res0: scala.collection.mutable.Set[Int] = Set()

// remove
scala> val set = scala.collection.mutable.Set(1, 2, 3, 4, 5)
set: scala.collection.mutable.Set[Int] = Set(2, 1, 4, 3, 5)

scala> set.remove(2)
res1: Boolean = true

scala> set
res2: scala.collection.mutable.Set[Int] = Set(1, 4, 3, 5)

scala> set.remove(40)
res3: Boolean = false

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2 匿名函數 ANONYMOUS FUNCTIONS

map
在上一節講到建立不同的集合,例如:

val ints = List(1,2,3)
val ints = List.range(1, 10)

匿名函數就像 mini 版的函數,例如可以對已有列表中的元素乘2,以建立一個新的列表

val ints = List(1,2,3)
scala> val doubledInts = ints.map(_ * 2)
doubledInts: List[Int] = List(2, 4, 6)

在這個例子中,程式碼_ * 2就是一個匿名函數,用簡短的方式說明將元素乘以 2

當然,上述程式碼val doubledInts = ints.map(_ * 2)也可以寫得稍微長點:

val doubledInts = ints.map((i: Int) => i * 2)
val doubledInts = ints.map(i => i * 2)

The character in Scala is something of a wildcard character. You’ll see it used in several different places. In this case it’s a shorthand way of saying, 「An element from the list, ints.」

上面的程式碼與下面 下麪 map 的 Java 程式碼是等效的

List<Integer> ints = new ArrayList<>(Arrays.asList(1, 2, 3));

// the `map` process
List<Integer> doubledInts = ints.stream()
                                .map(i -> i * 2)
                                .collect(Collectors.toList());

與下面 下麪的 Scala 程式碼類似:

val doubledInts = for (i <- ints) yield i * 2

filter method

// 使用 List
val ints = List.range(1, 10)

// 建立一個新 list, 裏面的值都大於給定值 5
val x = ints.filter(_ > 5)

// 建立一個新 list, 裏面的值都小於給定值 5
val x = ints.filter(_ < 5)

也可用下面 下麪的方式:

val x = ints.filter((i: Int) => i % 2 == 0)
val x = ints.filter(i => i % 2 == 0)
// REPL
scala> val x = ints.filter(_ > 5)
x: List[Int] = List(6, 7, 8, 9)

scala> val x = ints.filter(_ < 5)
x: List[Int] = List(1, 2, 3, 4)

scala> val x = ints.filter(_ % 2 == 0)
x: List[Int] = List(2, 4, 6, 8)

那麼 map 和 filter 是如何工作的呢?可以這樣理解,整數列表 List[Int] 呼叫 map 方法,map 中會傳入一個函數(或方法)將列表中的整數值轉換爲另一個整數值。如下在 map 中傳入一個函數

val ints = List(1,2,3)
def double(i: Int): Int = i * 2   //a method that doubles an Int
val doubledInts = ints.map(double)

上面兩行程式碼等價於下面 下麪一行

val doubledInts = ints.map(_ * 2)

同樣地,整數列表 List[Int] 呼叫 filter 方法時,filter 接收一個函數,返回值爲 Boolean 值。隨後對 ints 中的值進行過濾,只有返回爲 ture 的值纔會放入新的列表中

val ints = List.range(1, 10)

def lessThanFive(i: Int): Boolean = if (i < 5) true else false
// 簡化寫法
def lessThanFive(i: Int): Boolean = (i < 5)

val y = ints.filter(lessThanFive)

等價於

val y = ints.filter(_ < 5)

3 通用序列方法 Common Sequence Methods

使用序列內建方法意味着不必每次都費力寫 for loop 回圈遍歷元素

A great strength of the Scala collections classes is that they come with dozens of pre-built methods. The benefit of this is that you no longer need to write custom for loops every time you need to work on a collection. (If that’s not enough of a benefit, it also means that you no longer have to read custom for loops written by other developers.)
這些方法包括:

  • map
  • filter
  • foreach
  • head
  • tail
  • take, takeWhile
  • drop, dropWhile
  • find
  • reduce, fold

集合類 Array, ArrayBuffer, List, Vector, etc. 都可以使用接上述方法。下面 下麪以 List 爲例進行介紹

注意,這些方法並不能改變呼叫這些方法的集合本身,而是生成一個改變後的新集合
As a very important note, none of these methods mutate the collection that they’re called on. They all work in a functional style, so they return a new collection with the modified results.

// 後續方法基於在此建立的列表   REPL
scala> val nums = (1 to 10).toList
nums: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> val names = List("joel", "ed", "chris", "maurice")
names: List[String] = List(joel, ed, chris, maurice)

3.1 map

map 使用接收到的函數作用於列表中的每個元素,每次處理一個,最後返回處理之後的新列表
The map method steps through each element in the existing list, applying the algorithm you supply to each element, one at a time; it then returns a new list with all of the modified elements.

//  REPL
scala> val doubles = nums.map(_ * 2)
doubles: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

// 等價於
scala> val doubles = nums.map(i => i * 2)
doubles: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
scala> val capNames = names.map(_.capitalize)
capNames: List[String] = List(Joel, Ed, Chris, Maurice)

scala> val doubles = nums.map(_ * 2)
doubles: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

scala> val lessThanFive = nums.map(_ < 5)
lessThanFive: List[Boolean] = List(true, true, true, true, false, false, false, false, false, false)

As that last example shows, it’s perfectly legal (and very common) to use map to return a list with a different type (List[Boolean]) from the original type (List[Int]).

3.2 filter

filter 會過濾給定的列表,返回一個新建立的列表

// REPL
scala> val lessThanFive = nums.filter(_ < 5)
lessThanFive: List[Int] = List(1, 2, 3, 4)

scala> val evens = nums.filter(_ % 2 == 0)
evens: List[Int] = List(2, 4, 6, 8, 10)

scala> val shortNames = names.filter(_.length <= 4)
shortNames: List[String] = List(joel, ed)

3.3 foreach

foreach 用於遍歷列表中的每個元素, foreach 主要是用來處理更多的操作,例如列印:

// REPL
scala> names.foreach(println)
joel
ed
chris
maurice
// REPL
scala> nums.filter(_ < 4).foreach(println)
1
2
3

3.4 head

head 來自於函數語言程式設計語言 Lisp 中,用來列印列表中的第一個元素

scala> nums.head
res0: Int = 1

scala> names.head
res1: String = joel

String 是一個字元序列,可以把它當作一個列表:

scala> "foo".head
res2: Char = f

scala> "bar".head
res3: Char = b

空集合使用 head, 會拋出異常

scala> val emptyList = List[Int]()
val emptyList: List[Int] = List()

scala> emptyList.head
java.util.NoSuchElementException: head of empty list

3.5 tail

tail 同樣來自於函數語言程式設計語言 Lisp 中,用來列印除列表第一個元素之後的其它元素

scala> nums.tail
res0: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> names.tail
res1: List[String] = List(ed, chris, maurice)

也可以用在 String 上, 用在 空集合上會拋出異常

scala> "foo".tail
res2: String = oo

scala> "bar".tail
res3: String = ar

scala> emptyList.tail
java.lang.UnsupportedOperationException: tail of empty list

3.6 take, takeWhile

take 方法會從列表中取出你想要的元素,建立一個新的列表

// take
scala> nums.take(1)
res0: List[Int] = List(1)

scala> nums.take(2)
res1: List[Int] = List(1, 2)

scala> names.take(1)
res2: List[String] = List(joel)

scala> names.take(2)
res3: List[String] = List(joel, ed)
// takeWhile
scala> nums.takeWhile(_ < 5)
res4: List[Int] = List(1, 2, 3, 4)

scala> names.takeWhile(_.length < 5)
res5: List[String] = List(joel, ed)

3.7 drop, dropWhile

drop, dropWhile 本質上與 take, takeWhile 作用相反,刪除給定的元素然後返回剩下的

// drop
scala> nums.drop(1)
res0: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> nums.drop(5)
res1: List[Int] = List(6, 7, 8, 9, 10)

scala> names.drop(1)
res2: List[String] = List(ed, chris, maurice)

scala> names.drop(2)
res3: List[String] = List(chris, maurice)
// dropWhile
scala> nums.dropWhile(_ < 5)
res4: List[Int] = List(5, 6, 7, 8, 9, 10)

scala> names.dropWhile(_ != "chris")
res5: List[String] = List(chris, maurice)

3.8 reduce

reduce 與 map reduce 中的 reduce 操作類似。reduce 使用函數或匿名函數,作用於列表中的後續元素
It takes a function (or anonymous function) and applies that function to successive elements in the list.

理解 reduce

// 定義一個加法運算,有輸出資訊
def add(x: Int, y: Int): Int = {
    val theSum = x + y
    println(s"received $x and $y, their sum is $theSum")
    theSum
}

// 初始化建立一個列表
val a = List(1,2,3,4)

// 列表呼叫 reduce
scala> a.reduce(add)
received 1 and 2, their sum is 3
received 3 and 3, their sum is 6
received 6 and 4, their sum is 10
res0: Int = 10

reduce 會將 list 中的元素縮減到一個值,使用 add 方法,也就是計算 list 中的累加和

也可以使用下面 下麪的方法:

scala> a.reduce(_ + _)
res0: Int = 10
scala> a.reduce(_ * _)
res1: Int = 24

an important part to know about reduce is that — as its name implies — it’s used to reduce a collection down to a single value.

4 Map 類中的方法 COMMON MAP METHODS

4.1 不可變 Map immutable Map

// REPL
val m = Map(
    1 -> "a", 
    2 -> "b", 
    3 -> "c",
    4 -> "d"
)
// how to iterate over Map elements
scala> for ((k,v) <- m) printf("key: %s, value: %s\n", k, v)
key: 1, value: a
key: 2, value: b
key: 3, value: c
key: 4, value: d

// how to get the keys from a Map
scala> val keys = m.keys
keys: Iterable[Int] = Set(1, 2, 3, 4)

// how to get the values from a Map
scala> val values = m.values
val values: Iterable[String] = MapLike.DefaultValuesIterable(a, b, c, d)

// how to test if a Map contains a key
scala> val contains3 = m.contains(3)
contains3: Boolean = true

// how to transform Map values
scala> val ucMap = m.transform((k,v) => v.toUpperCase)
ucMap: scala.collection.immutable.Map[Int,String] = Map(1 -> A, 2 -> B, 3 -> C, 4 -> D)

// how to filter a Map by its keys
scala> val twoAndThree = m.view.filterKeys(Set(2,3)).toMap
twoAndThree: scala.collection.immutable.Map[Int,String] = Map(2 -> b, 3 -> c)

// how to take the first two elements from a Map
scala> val firstTwoElements = m.take(2)
firstTwoElements: scala.collection.immutable.Map[Int,String] = Map(1 -> a, 2 -> b)

4.2 可變 Map mutable Map

利用 mutable Mab , 可以對 Map 進行原地操作
Scala has a mutable Map class that you can modify in place

// REPL
val states = scala.collection.mutable.Map(
    "AL" -> "Alabama", 
    "AK" -> "Alaska"
)

// add elements with +=
states += ("AZ" -> "Arizona")
states += ("CO" -> "Colorado", "KY" -> "Kentucky")

// remove elements with -=
states -= "KY"
states -= ("AZ", "CO")

// update elements by reassigning them
states("AK") = "Alaska, The Big State"

// retain elements by supplying a function that operates on
// the keys and/or values
states.retain((k,v) => k == "AK")