Scala學習筆記5 (集合 Collections)
5.util包
5.1.架構
The following figure shows all collections in package scala.collection. These are all high-level abstract classes or traits, which generally have mutable as well as immutable implementations.
The main trait is Iterable, which is the supertrait of both mutable and immutable variations of sequences (
Sequences, classes that inherit from trait Seq, let you work with groups of data lined up in order. Because the elements are ordered, you can ask for the first element, second element, 103rd element, and so on.
scala.collection.immutable
The immutability helps you develop correct, efficient algorithms because you never need to make copies of a collection.
scala.collection.mutable
不可變(collection.immutable._) |
可變(collection.mutable._) |
Array |
ArrayBuffer |
List |
ListBuffer |
String |
StringBuilder |
/ |
LinkedList, DoubleLinkedList |
List |
MutableList |
/ |
Queue |
Array |
ArraySeq |
Stack |
Stack |
HashMap HashSet |
HashMap HashSet |
ArrayStack |
5.2.集合Array,List,Tuple
Array |
長度固定 |
元素可變 |
確定長度,後賦值; |
List |
長度固定 |
元素不可變 |
|
Tuple |
長度固定 |
元素不可變 |
常用於有多個返回值的函式;或者多個變數的同時定義 |
Scala 2.8中,3者的元素都可以混合不同的型別(轉化為Any型別);
Scala 2.7中,Array、List都不能混合型別,只有Tuple可以;
Arraysallow you to hold a sequence of elements and efficiently access an element at an arbitrary position, both to get
or update the element, with a zero-based index.
Listssupport fast addition and removal of items to the beginning of the list, but theydo not provide fast access
to arbitrary indexes because the implementation must iterate through the list linearly.
5.2.1.定義和初始化
5.2.1.1Array
val list1 = new Array[String](0) // Array()
val list2 = new Array[String](3) // Array(null, null, null)
val list3:Array[String] = new Array(3) // // Array(null, null, null)
val list1 = Array("a","b","c","d") // 相當於Array.apply("a","b","c","d")
定義一個型別為Any的Array:
val aa = Array[Any](1, 2)
或:
val aa: Array[Any] = Array(1, 2)
或:
val aa: Array[_] = Array(1, 2)
定義:
Array (1,3,5,7,9,11)
也可以用
Array[Int](1 to 11 by 2:_*)
暫時還沒有找到Range(例如 1 to 11 by 2)之後跟:_*的依據
Array對應的可變ArrayBuffer:
val ab = collection.mutable.ArrayBuffer[Int]()
ab += (1,3,5,7)
ab ++= List(9,11) // ArrayBuffer(1, 3, 5, 7, 9, 11)
ab toArray // Array (1, 3, 5, 7, 9, 11)
ab clear // ArrayBuffer()
5.2.1.2List
val list:List[Int] = List(1,3,4,5,6) // 或者 List(1 to 6:_*)
val list1 = List("a","b","c","d") // 或者 List('a' to 'd':_*) map (_.toString)
元素合併進List用::
val list2 = "a"::"b"::"c"::Nil // Nil是必須的
val list3 = "begin" :: list2 // list2不變,只能加在頭,不能加在尾
多個List合併用++,也可以用:::(不如++)
val list4 = list2 ++ "end" ++ Nil
val list4 = list2 ::: "end" :: Nil // 相當於 list2 ::: List("end")
當 import Java.util._ 之後會產生衝突,需要指明包
scala.List(1,2,3)
List對應的可變ListBuffer:
val lb = scala.collection.mutable.ListBuffer(1,2,3)
lb.append(4) // ListBuffer(1, 2, 3, 4)
val lb = collection.mutable.ListBuffer[Int]()
lb += (1,3,5,7)
lb ++= List(9,11) // ListBuffer(1, 3, 5, 7, 9, 11)
lb.toList // List(1, 3, 5, 7, 9, 11)
lb.clear // ListBuffer()
建議定義方式:
val head::body = List(4,"a","b","c","d")
// head: Any = 4
// body: List[Any] = List(a, b, c, d)
val a::b::c = List(1,2,3)
// a: Int = 1
// b: Int = 2
// c: List[Int] = List(3)
定義固定長度的List:
List.fill(10)(2) // List(2, 2, 2, 2, 2, 2, 2, 2, 2, 2)
Array.fill(10)(2) // Array(2, 2, 2, 2, 2, 2, 2, 2, 2, 2)
又如:
List.fill(10)(scala.util.Random.nextPrintableChar)
// List(?, =, ^, L, p, <, \, 4, 0, !)
List.fill(10)(scala.util.Random.nextInt(101))
// List(80, 45, 26, 75, 24, 72, 96, 88, 86, 15)
5.2.1.3Tuple
val t1 = ("a","b","c")
var t2 = ("a", 123, 3.14, new Date())
val (a,b,c) = (2,4,6)
最簡單的Tuple:
1->"hello world"
和下面的寫法是等價的:
(1, "hello world")
To access elements of a tuple, you can use method _1 to access
the first element, _2 to access the second, and so on:
scala> val v = (1, "Nick", 43)
res179: Int = 1
scala> v._2
res180: String = Nick
scala> v._3
res181: Int = 43
5.2.1.4Vector
Scala2.8為了提高list的隨機存取效率而引入的新集合型別(而list存取前部的元素快,越往後越慢)。
val v = Vector.empty
val v2 = 0 +: v :+ 10 :+ 20 // Vector(0, 10, 20), Vector 那一邊始終有":"
v2(1) // 10
v2 updated (1,100) // Vector(0, 100, 20)
這個例子舉的不太好,scala.collection.immutable. Vector擴充套件、updated之後是新生成的vector,原vector保持immutable。這點和List類似。
Seq的預設實現是List:
Seq(1,2,3) // List(1, 2, 3)
IndexSeq的預設實現是Vector:
IndexSeq(1,2,3) // Vector(1, 2, 3)
5.2.1.5Range
Range(0, 5) // (0,1,2,3,4)
等同於:
0 until 5
等同於:
0 to 4
兩個Range相加:
('0' to '9') ++ ('A' to 'Z') // (0,1,..,9,A,B,...,Z)
Range和序列轉換:
1 to 5 toList
相當與:
List(1 to 5:_*)
或者:
Vector(1 to 5: _*) // Vector(1,2,3,4,5)
5.2.1.6Stack Queue (List類的sibling)
先進後出的堆疊:
val s = collection.immutable.Stack()
You push an element onto a stack with push, pop an element with pop, and peek at the top of the stack without removing it with top/head.
val s2 = s.push(10,20,30) // Stack(30, 20, 10)
s2.head // 30
s2.pop.pop // Stack(10)
對應的可變Stack:
val ms = collection.mutable.Stack()
ms.push(1,3,5).push(7) // Stack(7, 5, 3, 1)
ms.head // 7
ms.pop // 7, ms = Stack(5,3,1)
先進先出的佇列:
val q = collection.immutable.Queue() // 也可指定型別 Queue[Int]()
//You can append an element to an immutable queue with enqueue:
val q2 = q.enqueue(0).enqueue(List(10,20,30)) // Queue(0, 10, 20, 30)
//To remove an element from the head of the queue, you use dequeue:
q2.dequeue._1 // 0
q2.dequeue._2 // Queue(10, 20, 30)
On immutable queues, the dequeue method returns a pair (a Tuple2)
consisting of the element at the head of the queue, and the rest of the queue with the head element removed.
val qHas123 = Queue(1,2,3)
scala> val (element, has23) = qHas123.dequeue element: Int = 1 has23: scala.collection.immutable.Queue[Int] = Queue(2,3)
對應的可變Queue:
You use a mutable queue similarly to how you use an immutable one, but instead of enqueue,
you use the += and++= operators
to append. Also, on a mutable queue, the dequeue method will just remove the head element from the queue and return it.
val mq = collection.mutable.Queue[Int]()
mq += (1,3,5)
mq ++= List(7,9) // Queue(1, 3, 5, 7, 9)
mq dequeue // 1, mq= Queue(3, 5, 7, 9)
mq clear // Queue()
If you need a first-in-first-out sequence, you can use a Queue.
If you need a last-in-first-out sequence, you can use a Stack.
5.2.1.7Stream
Stream相當於lazy List,避免在中間過程中生成不必要的集合。
定義生成:
val st = 1 #:: 2 #:: 3 #:: Stream.empty // Stream(1, ?)
例子:fib數列的Stream版本簡單易懂
def fib(a: Int, b: Int): Stream[Int] = a #:: fib(b, a+b)
val fibs = fib(1, 1).take(7).toList // List(1, 1, 2, 3, 5, 8, 13)
fib數列的前後項比值趨於黃金分割:
def fn(n:Int) = fib(1,1)(n)
1 to 10 map (n=> 1.0*fn(n)/fn(n+1)) // Vector(0.5, 0.666, ..., 0.618)
例子1:
Range (1,50000000).filter (_ % 13==0)(1) // 26, 但很慢,需要大量記憶體
Stream.range(1,50000000).filter(_%13==0)(1) // 26,很快,只計算最終結果需要的內容
注意:
第一個版本在filter後生成一箇中間collection,size=50000000/13;而後者不生成此中間collection,只計算到26即可。
例子2:
(1 to 100).map(i=> i*3+7).filter(i=> (i%10)==0).sum // map和filter生成兩個中間collection
(1 to 100).toStream.map(i=> i*3+7).filter(i=> (i%10)==0).sum
5.2.2.使用(map, flatMap, filter, exists等)
5.2.2.1map
// 型別可以混合:
import java.util._
val list3 = Array("a", 123, 3.14, new Date())
List("a","b","c").map(s=> s.toUpperCase()) // 方式1
List("a","b","c").map(_.toUpperCase()) // 方式2, 類似於Groovy的it
// = List(A, B, C)
5.2.2.2filter filterNot
List(1,2,3,4,5).filter(_%2==0) // List(2, 4)
也可以寫成:
for (x<-List(1,2,3,4,5) if x%2==0) yield x
List(1,2,3,4,5).filterNot(_%2==0) // List(1, 3, 5)
5.2.2.3partition span splitAt groupBy
注:val (a,b) = List(1,2,3,4,5).partition(_%2==0) // (List(2,4), List(1,3,5))
可把Collection分成:滿足條件的一組,其他的另一組。
和partition相似的是span,但有不同:
List(1,9,2,4,5).span(_<3) // (List(1),List(9, 2, 4, 5)),碰到不符合就結束
List(1,9,2,4,5).partition(_<3) // (List(1, 2),List(9, 4, 5)),掃描所有
List(1,3,5,7,9) splitAt 2 // (List(1, 3),List(5, 7, 9))
List(1,3,5,7,9) groupBy (5<) // Map((true,List(7, 9)), (false,List(1, 3, 5)))
5.2.2.4foreach
列印:
Array("a","b","c","d").foreach(printf("[%s].",_))
// [a].[b].[c].[d].
5.2.2.5exists
// 集合中是否存在符合條件的元素
List(1,2,3,4,5).exists(_%3==0) // true
5.2.2.6find
返回序列中符合條件的第一個。
例子:查詢整數的第一個因子(最小因子、質數)
def fac1(n:Int) = if (n>= -1 && n<=1) n else (2 to n.abs) find (n%_==0) get
5.2.2.7sorted sortWith sortBy
例子(排序):
List(1,3,2,0,5,9,7).sorted // List(0, 1, 2, 3, 5, 7, 9)
List(1,3,2,0,5,9,7).sortWith(_>_) // List(9, 7, 5, 3, 2, 1, 0)
List("abc", "cb", "defe", "z").sortBy(_.size) // List(z, cb, abc, defe)
List((1,'c'), (1,'b'), (2,'a')) .sortBy(_._2) // List((2,a), (1,b), (1,c))
5.2.2.8distinct
例子:(去除List中的重複元素)
def uniq[T](l:List[T]) = l.distinct
uniq(List(1,2,3,2,1)) // List(1,2,3)
5.2.2.9flatMap
flatMap的作用:把多層次的資料結構“平面化”,並去除空元素(如None)。
可用於:得到xml等樹形結構的所有節點名稱,去除None等
例子1a:(兩個List做乘法)
List(1,2,3) * List(10,20,30) = List(10, 20, 30, 20, 40, 60, 30, 60, 90)
val (a,b) = (List(1,2,3), List(10,20,30))
a flatMap (i=> b map (j=> i*j))
等同於:
for (i<-a; i<-b) yield i*j // 這個寫法更清晰
例子1b:
如果不用flatMap而是用map,結果就是: