Scala 系列(六)—— 常用集合型別之 List & Set
一、List字面量
List 是 Scala 中非常重要的一個資料結構,其與 Array(陣列) 非常類似,但是 List 是不可變的,和 Java 中的 List 一樣,其底層實現是連結串列。
scala> val list = List("hadoop","spark","storm")
list: List[String] = List(hadoop,spark,storm)
// List 是不可變
scala> list(1) = "hive"
<console>:9: error: value update is not a member of List[String]
複製程式碼
二、List型別
Scala 中 List 具有以下兩個特性:
- 同構 (homogeneous):同一個 List 中的所有元素都必須是相同的型別;
-
協變 (covariant):如果 S 是 T 的子型別,那麼
List[S]
就是List[T]
的子型別,例如List[String]
是List[Object]
的子型別。
需要特別說明的是空列表的型別為 List[Nothing]
:
scala> List()
res1: List[Nothing] = List()
複製程式碼
三、構建List
所有 List 都由兩個基本單元構成:Nil
和 ::
(讀作"cons")。即列表要麼是空列表 (Nil),要麼是由一個 head 加上一個 tail 組成,而 tail 又是一個 List。我們在上面使用的 List("hadoop","spark","storm")
"hadoop"::"spark":: "storm"::Nil
。
scala> val list01 = "hadoop"::"spark":: "storm"::Nil
list01: List[String] = List(hadoop,storm)
// :: 操作符號是右結合的,所以上面的表示式和下面的等同
scala> val list02 = "hadoop"::("spark":: ("storm"::Nil))
list02: List[String] = List(hadoop,storm)
複製程式碼
四、模式匹配
Scala 支援展開列表以實現模式匹配。
scala> val list = List("hadoop",storm)
scala> val List(a,b,c)=list
a: String = hadoop
b: String = spark
c: String = storm
複製程式碼
如果只需要匹配部分內容,可以如下:
scala> val a::rest=list
a: String = hadoop
rest: List[String] = List(spark,storm)
複製程式碼
五、列表的基本操作
5.1 常用方法
object ScalaApp extends App {
val list = List("hadoop","storm")
// 1.列表是否為空
list.isEmpty
// 2.返回列表中的第一個元素
list.head
// 3.返回列表中除第一個元素外的所有元素 這裡輸出 List(spark,storm)
list.tail
// 4.tail 和 head 可以結合使用
list.tail.head
// 5.返回列表中的最後一個元素 與 head 相反
list.init
// 6.返回列表中除了最後一個元素之外的其他元素 與 tail 相反 這裡輸出 List(hadoop,spark)
list.last
// 7.使用下標訪問元素
list(2)
// 8.獲取列表長度
list.length
// 9. 反轉列表
list.reverse
}
複製程式碼
5.2 indices
indices 方法返回所有下標。
scala> list.indices
res2: scala.collection.immutable.Range = Range(0,1,2)
複製程式碼
5.3 take & drop & splitAt
- take:獲取前 n 個元素;
- drop:刪除前 n 個元素;
- splitAt:從第幾個位置開始拆分。
scala> list take 2
res3: List[String] = List(hadoop,spark)
scala> list drop 2
res4: List[String] = List(storm)
scala> list splitAt 2
res5: (List[String],List[String]) = (List(hadoop,spark),List(storm))
複製程式碼
5.4 flatten
flatten 接收一個由列表組成的列表,並將其進行扁平化操作,返回單個列表。
scala> List(List(1,2),List(3),List(),List(4,5)).flatten
res6: List[Int] = List(1,2,3,4,5)
複製程式碼
5.5 zip & unzip
對兩個 List 執行 zip
操作結果如下,返回對應位置元素組成的元組的列表,unzip
則執行反向操作。
scala> val list = List("hadoop","storm")
scala> val score = List(10,20,30)
scala> val zipped=list zip score
zipped: List[(String,Int)] = List((hadoop,10),(spark,20),(storm,30))
scala> zipped.unzip
res7: (List[String],List[Int]) = (List(hadoop,storm),List(10,30))
複製程式碼
5.6 toString & mkString
toString 返回 List 的字串表現形式。
scala> list.toString
res8: String = List(hadoop,storm)
複製程式碼
如果想改變 List 的字串表現形式,可以使用 mkString。mkString 有三個過載方法,方法定義如下:
// start:字首 sep:分隔符 end:字尾
def mkString(start: String,sep: String,end: String): String =
addString(new StringBuilder(),start,sep,end).toString
// seq 分隔符
def mkString(sep: String): String = mkString("","")
// 如果不指定分隔符 預設使用""分隔
def mkString: String = mkString("")
複製程式碼
使用示例如下:
scala> list.mkString
res9: String = hadoopsparkstorm
scala> list.mkString(",")
res10: String = hadoop,storm
scala> list.mkString("{",",","}")
res11: String = {hadoop,storm}
複製程式碼
5.7 iterator & toArray & copyToArray
iterator 方法返回的是迭代器,這和其他語言的使用是一樣的。
object ScalaApp extends App {
val list = List("hadoop","storm")
val iterator: Iterator[String] = list.iterator
while (iterator.hasNext) {
println(iterator.next)
}
}
複製程式碼
toArray 和 toList 用於 List 和陣列之間的互相轉換。
scala> val array = list.toArray
array: Array[String] = Array(hadoop,storm)
scala> array.toList
res13: List[String] = List(hadoop,storm)
複製程式碼
copyToArray 將 List 中的元素拷貝到陣列中指定位置。
object ScalaApp extends App {
val list = List("hadoop","storm")
val array = Array("10","20","30")
list.copyToArray(array,1)
println(array.toBuffer)
}
// 輸出 :ArrayBuffer(10,hadoop,spark)
複製程式碼
六、列表的高階操作
6.1 列表轉換:map & flatMap & foreach
map 與 Java 8 函式語言程式設計中的 map 類似,都是對 List 中每一個元素執行指定操作。
scala> List(1,3).map(_+10)
res15: List[Int] = List(11,12,13)
複製程式碼
flatMap 與 map 類似,但如果 List 中的元素還是 List,則會對其進行 flatten 操作。
scala> list.map(_.toList)
res16: List[List[Char]] = List(List(h,a,d,o,p),List(s,p,r,k),t,m))
scala> list.flatMap(_.toList)
res17: List[Char] = List(h,s,k,m)
複製程式碼
foreach 要求右側的操作是一個返回值為 Unit 的函式,你也可以簡單理解為執行一段沒有返回值程式碼。
scala> var sum = 0
sum: Int = 0
scala> List(1,5) foreach (sum += _)
scala> sum
res19: Int = 15
複製程式碼
6.2 列表過濾:filter & partition & find & takeWhile & dropWhile & span
filter 用於篩選滿足條件元素,返回新的 List。
scala> List(1,5) filter (_ % 2 == 0)
res20: List[Int] = List(2,4)
複製程式碼
partition 會按照篩選條件對元素進行分組,返回型別是 tuple(元組)。
scala> List(1,5) partition (_ % 2 == 0)
res21: (List[Int],List[Int]) = (List(2,4),List(1,5))
複製程式碼
find 查詢第一個滿足條件的值,由於可能並不存在這樣的值,所以返回型別是 Option
,可以通過 getOrElse
在不存在滿足條件值的情況下返回預設值。
scala> List(1,5) find (_ % 2 == 0)
res22: Option[Int] = Some(2)
val result: Option[Int] = List(1,5) find (_ % 2 == 0)
result.getOrElse(10)
複製程式碼
takeWhile 遍歷元素,直到遇到第一個不符合條件的值則結束遍歷,返回所有遍歷到的值。
scala> List(1,-4,5) takeWhile (_ > 0)
res23: List[Int] = List(1,3)
複製程式碼
dropWhile 遍歷元素,直到遇到第一個不符合條件的值則結束遍歷,返回所有未遍歷到的值。
// 第一個值就不滿足條件,所以返回列表中所有的值
scala> List(1,5) dropWhile (_ < 0)
res24: List[Int] = List(1,5)
scala> List(1,5) dropWhile (_ < 3)
res26: List[Int] = List(3,5)
複製程式碼
span 遍歷元素,直到遇到第一個不符合條件的值則結束遍歷,將遍歷到的值和未遍歷到的值分別放入兩個 List 中返回,返回型別是 tuple(元組)。
scala> List(1,5) span (_ > 0)
res27: (List[Int],List[Int]) = (List(1,3),List(-4,5))
複製程式碼
6.3 列表檢查:forall & exists
forall 檢查 List 中所有元素,如果所有元素都滿足條件,則返回 true。
scala> List(1,5) forall ( _ > 0 )
res28: Boolean = false
複製程式碼
exists 檢查 List 中的元素,如果某個元素已經滿足條件,則返回 true。
scala> List(1,5) exists (_ > 0 )
res29: Boolean = true
複製程式碼
6.4 列表排序:sortWith
sortWith 對 List 中所有元素按照指定規則進行排序,由於 List 是不可變的,所以排序返回一個新的 List。
scala> List(1,-3,6) sortWith (_ < _)
res30: List[Int] = List(-3,6)
scala> val list = List( "hive","azkaban","hadoop")
list: List[String] = List(hive,azkaban,hadoop)
scala> list.sortWith(_.length>_.length)
res33: List[String] = List(azkaban,hive)
複製程式碼
七、List物件的方法
上面介紹的所有方法都是 List 類上的方法,下面介紹的是 List 伴生物件中的方法。
7.1 List.range
List.range 可以產生指定的前閉後開區間內的值組成的 List,它有三個可選引數: start(開始值),end(結束值,不包含),step(步長)。
scala> List.range(1,5)
res34: List[Int] = List(1,4)
scala> List.range(1,9,2)
res35: List[Int] = List(1,5,7)
scala> List.range(9,-3)
res36: List[Int] = List(9,6,3)
複製程式碼
7.2 List.fill
List.fill 使用指定值填充 List。
scala> List.fill(3)("hello")
res37: List[String] = List(hello,hello,hello)
scala> List.fill(2,3)("world")
res38: List[List[String]] = List(List(world,world,world),List(world,world))
複製程式碼
7.3 List.concat
List.concat 用於拼接多個 List。
scala> List.concat(List('a','b'),List('c'))
res39: List[Char] = List(a,c)
scala> List.concat(List(),List('b'),List('c'))
res40: List[Char] = List(b,c)
scala> List.concat()
res41: List[Nothing] = List()
複製程式碼
八、處理多個List
當多個 List 被放入同一個 tuple 中時候,可以通過 zipped 對多個 List 進行關聯處理。
// 兩個 List 對應位置的元素相乘
scala> (List(10,List(3,5)).zipped.map(_ * _)
res42: List[Int] = List(30,80)
// 三個 List 的操作也是一樣的
scala> (List(10,5),List(100,200)).zipped.map(_ * _ + _)
res43: List[Int] = List(130,280)
// 判斷第一個 List 中元素的長度與第二個 List 中元素的值是否相等
scala> (List("abc","de"),2)).zipped.forall(_.length == _)
res44: Boolean = true
複製程式碼
九、緩衝列表ListBuffer
上面介紹的 List,由於其底層實現是連結串列,這意味著能快速訪問 List 頭部元素,但對尾部元素的訪問則比較低效,這時候可以採用 ListBuffer
,ListBuffer 提供了在常量時間內往頭部和尾部追加元素。
import scala.collection.mutable.ListBuffer
object ScalaApp extends App {
val buffer = new ListBuffer[Int]
// 1.在尾部追加元素
buffer += 1
buffer += 2
// 2.在頭部追加元素
3 +=: buffer
// 3. ListBuffer 轉 List
val list: List[Int] = buffer.toList
println(list)
}
//輸出:List(3,1,2)
複製程式碼
十、集(Set)
Set 是不重複元素的集合。分為可變 Set 和不可變 Set。
10.1 可變Set
object ScalaApp extends App {
// 可變 Set
val mutableSet = new collection.mutable.HashSet[Int]
// 1.新增元素
mutableSet.add(1)
mutableSet.add(2)
mutableSet.add(3)
mutableSet.add(3)
mutableSet.add(4)
// 2.移除元素
mutableSet.remove(2)
// 3.呼叫 mkString 方法 輸出 1,3,4
println(mutableSet.mkString(","))
// 4. 獲取 Set 中最小元素
println(mutableSet.min)
// 5. 獲取 Set 中最大元素
println(mutableSet.max)
}
複製程式碼
10.2 不可變Set
不可變 Set 沒有 add 方法,可以使用 +
新增元素,但是此時會返回一個新的不可變 Set,原來的 Set 不變。
object ScalaApp extends App {
// 不可變 Set
val immutableSet = new collection.immutable.HashSet[Int]
val ints: HashSet[Int] = immutableSet+1
println(ints)
}
// 輸出 Set(1)
複製程式碼
10.3 Set間操作
多個 Set 之間可以進行求交集或者合集等操作。
object ScalaApp extends App {
// 宣告有序 Set
val mutableSet = collection.mutable.SortedSet(1,5)
val immutableSet = collection.immutable.SortedSet(3,7)
// 兩個 Set 的合集 輸出:TreeSet(1,2,4,5,6,7)
println(mutableSet ++ immutableSet)
// 兩個 Set 的交集 輸出:TreeSet(3,5)
println(mutableSet intersect immutableSet)
}
複製程式碼
參考資料
- Martin Odersky . Scala 程式設計 (第 3 版)[M] . 電子工業出版社 . 2018-1-1
- 凱.S.霍斯特曼 . 快學 Scala(第 2 版)[M] . 電子工業出版社 . 2017-7
更多大資料系列文章可以參見 GitHub 開源專案: 大資料入門指南