seq set map都是继承iterable,而iterable指的是那些能生成用来访问集合中的所有元素的的iterator的集合:
val coll = ...//某种iterable
val iter = coll.iterator
while(iter.hasNext)
对iter.next()执行某种操作
这是遍历一个集合最基本的方式。
seq是一个有先后次序的值的序列,比如数组或列表。indexedseq允许我们通过整型下标快速的访问任意元素。举例来说,arraybuffer是带下标的,但链表不是。
set是一组没有先后次序的值,在sortedset中,元素以某种排过序的顺序被访问。
map是一组(键,值)对偶。sortedmap按照键的排序访问其中的实体。
这个继承层级和java很相似,同时有一些不错的改进:
1 映射隶属于同一个继承层级而不是一个单独的层级关系。
2indexedseq是数组的超类型,但不是列表的超类型,以便区分。
每个scala集合特质或类都有一个带有apply方法的伴生对象,这个apply方法可以用来构建该集合中的实例。
例如
Iterable(0xFF, 0xFF00, 0xFF0000)
Set(Color.RED, Color.GREEN, Color.BLUE)
Map(Color.RED -> 0xFF0000, Color.GREEN -> 0xFF00, Color.BLUE -> 0xFF)
SortedSet("Hello","World")
这样的设计叫做“统一创建原则”。
scala同时支持可变和不可变集合。不可变的集合从不改变,因此你可以安全地共享其引用,甚至是在一个多线程的应用程序当中也没有问题。
举例来说,既有scala.collection.mutable.Map,也有scala.collection.immutable.Map。它们有一个共同的超类型scala.collection.Map(当然了,这个超类型没有定义任何改值操作)。
当你握有一个指向scala.collection.immutable.Map的引用时,你知道没有人能修改这个映射。如果你有的是一个scala.collection.Map,那么你不能改变它,但别人也许会。
scala优先采用不可变集合。scala.collection包中的伴生对象产出不可变集合。举例来说,scala.collection.Map("Hello" -> 42)是一个不可变的映射。
不止如此,总被引出的scala包和Predef对象里有指向不可变特质的类别别名List,Set和Map,举例来说,Predef.Map和scala.collection.immutable.Map是一回事。
使用import scala.collection.mutable
你就可以用map得到不可变的映射,用mutable.Map得到可变的映射。
不可变集合的关键在于你可以基于老的集合创建新的集合。举例来说,如果numbers是一个不可变的集,那么numbers + 9 就是一个包含了numbers和9的新集。如果9已经在集中,则你得到的是指向老集的引用。这在递归计算中特别自然。举例来说,我们可以计算某个整数中所有出现过的阿拉伯数字的集:
def digits(n: Int): Set[Int] =
if (n < 0) digits(-n)
else if (n < 10) Set(n)
else digit(n / 10) + (n % 10)
这个方法从包含单个数字的集开始,每一步,添加进另外一个数字,不过,添加某个数字并不会改变原有的集合,而是构造出一个新的集。
序列
不可变序列:indexedseq list stream stack queue都继承了seq,而vector range继承了indexedseq
vector是arraybuffer的不可变版本,一个带有下标的序列,支持快速随机访问。向量是以树形结构的形式实现的,每个节点可以有不超过32个子节点。对于一个有100万个元素的向量而言,我们只需要四层节点,因为10的6次方约等于32的四次方。访问这样一个列表中的某个越苏只需要4跳,而在链表中,同样的操作平均需要500000跳。
range表示一个整数序列,比如0,1,2,3,4,5。当然了,range对象并不存储所有值而只是起始值,结束值和增值。你可以用to和until方法来构造range对象。
可变序列:indexedseq stack queue priorityQueue LinkedList DoubleLinkedList继承seq 而arraybuffer继承indexedseq
列表
在scala中,列表要么是Nil(即空表),要么是一个head元素加一个tail,而tail又是一个列表。比如下面一个列表 val digits = List(4,2)
digits.head的值是4,而digits.tail是List(2)。再进一步,digits.tail.head是2,而digits.tail.tail是Nil。
::操作符从给定的头和尾创建一个新的列表,例如 9 ::List(4, 2) 就是List(9,4,2)你也可以将这个列表写做 9 :: 4 ::2 :: Nil
注意::是右结合的。通过::操作符,列表将从末端开始构建。
9 :: (4 :: (2 :: Nil)))
在java或c++中,我们用迭代器来遍历链表。在scala中你也可以这样做,但使用递归会更加自然。例如如下函数计算整数链表中所有元素的和:
def sum(lst : List[Int]): Int =
if( lst ==Nil) 0 else lst.head + sum(lst.tail)
或者如果你愿意,你也可以使用模式匹配:
def sum(lst: List[Int]):Int = lst match{
case Nil => 0
case h :: t=> h+sum(t)//h是lst.head而t是lst.tail
}
注意第二个模式中::操作符,它将列表“析构"成头部和尾部
递归之所以那么自然,是因为列表的尾部正好又是一个列表。
不过,在你对递归的优雅过度兴奋之前,先看看scala类库。它已经有sum方法了:List(9, 4. 2).sum //输出15
可变列表
可变的LinkedList和不可变的List相似,只不过你可以通过对elem引用赋值来修改其头部,对next引用赋值来修改其尾部。
注意,你并不是给head和tail赋值
举例来说,如下循环将把所有负值都改成0:
val lst = scala.collection.mutable.LinkedList(1, -2, 7, -9)
var cur = lst
while(cur != Nil){
if (cur.elem < 0) cur.elem = 0
cur = cur.next
}
如下循环将去除每两个元素中的一个:
var cur = lst
while(cur != Nil && cur.next != Nil){
cur.next = cur.next.next
cur = cur.next
}
在这里,变量cur用起来就像是迭代器,但实际上它的类型是LinkedList
除了LinkedList外,scala还提供了一个DoubleLinkedList,区别是它多带一个prev引用。
注意如果你想要把列表中的某个节点变成列表中最后一个节点,你不能将next引用设为Nil,而应该将它设为LinkedList.empty。也不要将它设为null,不然你在遍历该链表时会遇到空指针错误。
集:
集是不重复元素的集合。尝试将已有元素加入没有效果,例如,Set(2, 0, 1) + 1和Set(2, 0, 1) 是一样的。
和列表不同,集并不保留元素插入的顺序。缺省情况下,集是以哈希集实现的,其元素根据hashcode方法的值进行组织(scala和java一样,每个对象都有hashcode方法)
举例来说,如果你遍历
Set(1, 2, 3, 4, 5, 6)
元素·被访问到的次序为5,1,6,2,3,4
你可能会觉得奇怪为什么集不保存元素的顺序。实际情况是,如果你允许集对他们的元素重新排列的话,你可以以块很多的速度找到元素。在哈希集中查找元素要比在数组或列表中快得多。
链式哈希集可以记住元素被插入的顺序。它会维护一个链表来达到这个目的。例如
val weekdays = scala.collection.mutable.LinkedHashSet("Mo","Tu","We","Th","Fr")
如果你想要按照已排序的顺序来访问集中的元素,用已排序的集:
scala.collection.immutable.SortedSet(1, 2, 3, 4, 5, 6)
已排序的集是用红黑树实现的。
注意scala2.9没有可变的已排序集,如果你需要这样一个数据结构,可以用java.util.TreeSet。
位集(bit set)是集的一种实现,以一个字位序列的方式存放非负整数。如果集中有i,则第i个字位是1,。这是个很高效的实现,只要最大元素不是特别大。scala提供了可变的和不可变的两个BitSet类。
contains方法检查某个集是否包含给定的值。subsetOf方法检查某个集当中的所有元素是否都被另一个集包含。
val digits = Set(1, 7, 2, 9)
digits contains 0 //false
Set(1, 2) subsetOf digits // true
union intersect和diff方法执行通常是集操作。如果你愿意,你也可以将他们写做| & &~ 你也可以将联合(union)写做++,将diff写做--。
用于添加或去除元素的操作符有很多,一般来说,+用来将元素添加到无先后次序的集合,而+:和:+则是将元素添加到有先后次序的集合的开头或末尾
Vector(1,2,3) :+ 5 //产出Vector(1,2,3,5)
1 +: Vector(1, 2,3) //产出Vector(1,1,2,3)
注意,和其他以冒号结尾的操作符一样,+:是右结合的,是右侧操作元的方法。
这些操作都返回新的集合(和原集合类型保持一致),不会修改原有的集合。而可变集合有+=操作符用于修改左侧操作元。例如:
val numbers = ArrayBuffer(1, 2, 3)
numbers += 5 //将5添加到numbers
对于不可变集合,你可以在var上使用+=或:+=,就像这样
var numbers = Set(1, 2, 3)
numbers += 5//将numbers设为不可变的集 numbers + 5
var numberVector = Vector(1, 2, 3)
numberVector :+= 5//在这里我们没法用+=,因为向量没有+操作。
要移除元素,使用-操作符:
Set(1,2,3) - 2//将产出Set(1, 3)
你也可以用++来一次添加多个元素:
coll ++ coll2
将产出一个与coll类型相同,且包含了coll和coll2中所有元素的集合。类似的,--操作符将一次移除多个元素。