1. 程式人生 > >Scala學習筆記5 (集合 Collections)

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 (

Seqs), sets, and maps. Sequences are ordered collections, such as arrays and lists. Sets contain at most one of each object, as determined by the == method. Maps contain a collection of keys mapped to values.

<image not found>

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

<image not found>

The immutability helps you develop correct, efficient algorithms because you never need to make copies of a collection. 

scala.collection.mutable

<image not found>

不可變(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中,ArrayList都不能混合型別,只有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")

定義一個型別為AnyArray

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)

scala> v._1

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後生成一箇中間collectionsize=50000000/13;而後者不生成此中間collection,只計算到26即可。

例子2

(1 to 100).map(i=> i*3+7).filter(i=> (i%10)==0).sum // mapfilter生成兩個中間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, 類似於Groovyit

// = 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, 3020, 40, 6030, 60, 90)

val (a,b) = (List(1,2,3), List(10,20,30))

flatMap (i=> b map (j=> i*j))

等同於:

for (i<-a; i<-b) yield i*j // 這個寫法更清晰

例子1b

如果不用flatMap而是用map,結果就是: