本文链接:Scala学习(十二)——集合类型
简介
Java和C++里提供了十分方便的集合类供我们使用,Scala也不例外。
使用Java中的集合类
因为Scala可以直接使用Java库中的类,所以直接使用Java中的集合类也是可以的。用法和Java几乎完全相同:
1 2 3 4 |
import java.util.ArrayList val a = new ArrayList[Int] a.add(123) println(a.get(0)) // 123 |
但是,如果只是这样的话,就没必要写这篇文章了。Scala也提供了自已的集合类型,相比之下,在Scala中用这些类型才更好。
数组Array
和Java不同,Scala中没有数组类型这一设定。Scala的数组是名为Array
的类。但是不要担心效率问题,在编译时,会变成JVM中的数组的。
Array
数组的特征:长度不可变,可随机访问。
准确来说,数组不能算作是集合,只能算作数据结构而已。
下面是一个使用数组的例子:
1 2 3 |
val arr = new Array[Int](10) // 长度为10的Int数组 arr(0) = 4 // 赋值 println(arr(3)) // 使用 |
Scala中,也可以从值构建数组:
1 2 |
var arr = Array(4, 5, 6) // 注意没有new关键字 println(arr(2)) // 6 |
集合Set
(注:本章的“集合”指Set
,本文的标题中的“集合”指collection,比Set
含义要宽)
以是否可编辑来区分,集合有可变和不可变两种;以实现方式区分,集合有HashSet
和TreeSet
等几种。这里我们主要关心的是集合的抽象性质,所以用前者来区分。至于后者,在使用时将Set
类型替换成相应类型即可。
集合Set
的特征:不存在重复元素(用##
和==
来判断是否相等),无序(不可随机访问),判断对象是否存在于Set
中时一般较快(取决于实现)。
创建集合有两种方法,一是用new
关键字新建,二是用Set
对象的apply
方法。用apply
方法的好处是可以直接赋值。
1 2 3 4 5 6 7 |
import scala.collection.immutable import scala.collection.mutable val a = new immutable.HashSet[Int] // 直接new val b = new mutable.HashSet[String] val c = immutable.Set[Int](1, 2, 3) // 用工厂方法 val d = mutable.HashSet[Int](1, 2, 3) |
不可变的Set
有以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 |
import scala.collection.immutable val a = new immutable.HashSet[Int] // {} val b = a + 3 + 4 + 5 // 增加元素 {3, 4, 5} val c = b - 5 // 去除元素 {3, 4} val d = immutable.Set(2, 5) // {2, 5} val e = b ++ d // 增加集合 {2, 3, 4, 5} val f = b -- d // 去除集合 {3, 4} val g = b & d // 与 {5} val h = b | d // 或 {2, 3, 4, 5} val i = b &~ d // 先非d,后与 {3, 4} val j = a(4) // 判断有无元素,返回true |
值得注意的是以上每一个语句都新建了一个Set
对象,而不曾修改参与运算的对象的内容,正是符合了不可变的要求。对于可变类型,可以这样使用:
1 2 3 4 5 |
val a = new mutable.HashSet[Int] // {} a += 1 // 变为 {1} a ++= Set(1, 2, 3) // 变为 {1, 2, 3} a -= 3 // 变为 {1, 2} a --= Array(2, 3) // 变为 {1} |
链表List
和Java里不同,Scala中List
一定是以链表方式实现的。Java中的List
概念对应Scala中的Seq
。
链表的特征:元素有先后之分,适合逐个访问。
不可变链表的使用方法:
1 2 3 4 5 6 7 |
val a = List(1, 2) // 不可变链表 [1, 2] val b = 2 +: a // 在前端添加元素 [2, 1, 2] val c = b :+ 7 // 在后端添加元素 [2, 1, 2, 7] val d = List(2, 3) ++: a // 在前端添加元素列 [2, 3, 1, 2] val e = a ++: List(2, 3) // 在后端添加元素列 [1, 2, 2, 3] val f = e.drop(1) // 返回去掉前n个元素的链表 [2, 2, 3] val g = e.take(2) // 返回选取前n个元素的链表 [1, 2] |
可变链表的更多使用方法:
1 2 3 4 |
val a = mutable.MutableList(1, 2) // [1, 2] 2 +=: a // 左添加,变为 [2, 1, 2] a += 3 // 右添加,变为 [2, 1, 2, 3] a ++= List(3, 4) // 右添加,变为 [2, 1, 2, 3, 3, 4] |
可变数组ArrayBuffer
Scala中的这个类更像是Java中常用的ArrayList
类。
特征是:元素有先后之分,可以重复,可以随机访问,但是在中间插入时效率不高。
用法见下例:
1 2 3 4 5 6 7 8 9 10 11 |
val a = new mutable.ArrayBuffer[Int] // [] // ArrayBuffer 也有+, ++等运算,此处不再详述 a += 2 // 在后面添加 [2] a += 5 // [2, 5] 4 +=: a // 在前面添加 [4, 2, 3] a ++= Array(7, 5) // 在后面添加另一个集合 [4, 2, 5, 7, 5] mutable.ArrayBuffer(1, 2) ++=: a // 在前面添加另一个集合 [1, 2, 4, 2, 5, 7, 5] a -= 2 // 删除第一个出现的某元素 [1, 4, 2, 5, 7, 5] a --= Array(1, 5) // 逐个删除元素 [4, 2, 7, 5] a.remove(2) // 删除某下标的元素 [4, 2, 5] a.insert(1, 3) // 在中间插入 [4, 3, 2, 5] |
映射Map
Scala中的Map
和Java中功能类似。
和Scala中其他的集合类型也很类似,有可变和不可变之分,也有HashMap
等不同类型的实现,也有+
,++
之类的操作。下面给出一些其他集合没有的操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import scala.collection.immutable val a = immutable.Map( "one" -> 1, "two" -> 2, "san" -> 3 ) println(a("one")) // 用apply访问元素,1 println(a("san")) // 3 //println(a("four")) // 异常 println(a.contains("five")) // false import scala.collection.mutable val b = new mutable.HashMap[String, Double] b += "one point five" -> 1.5 // 添加映射 b -= "one point five" // 删除映射 |
其中的"one" -> 1
是一个特殊的用法,用于创建一个Tuple2
,和("one", 1)
所等价。也就是说,Map
储存了一系列的键值对,然后对键建立了索引,以达到快速访问的效果。
总结
Scala提供了和其他语言相同的基本集合类型以方便程序员使用,但和其他语言又有些许不同。
一,Scala中的集合类型有可变和不可变之分。这是因为函数式编程鼓励使用不可变量,以保持方法的函数性质。而可变量又是兼容了其他非函数式语言的设计。
二,Scala的集合大量使用了重载运算符来完成集合操作。对这一点我的看法是,虽然简洁了,但不那么显然了。所幸Scala也提供了函数名版的操作方法,所以也可以不使用运算符。
然后,整理一下这些类型的特点:
类型 | 长度可变 | 内容可变 | 可重复 | 插入 | 删除 | 按下标/键访问 | 按值访问 | 备注 |
---|---|---|---|---|---|---|---|---|
Array | 否 | 是 | 是 | – | – | 快 | 慢 | |
Set | 否 | 否 | 否 | – | – | – | 快 | |
mutable.Set | 是 | 是 | 否 | 快 | 快 | – | 快 | |
List | 否 | 否 | 是 | – | – | 慢 | 慢 | 访问头尾时快 |
MutableList | 是 | 是 | 是 | 慢 | 慢 | 慢 | 慢 | 访问和增删头尾时快 |
ArrayBuffer | 是 | 是 | 是 | 慢 | 慢 | 快 | 慢 | 增删尾部快 |
Map | 否 | 否 | 否/是 | – | – | 快 | 慢 | 值可重复 |
mutable.Map | 是 | 是 | 否/是 | 快 | 快 | 快 | 慢 |
以及一些操作的运算符:
运算符 | 作用 |
---|---|
+ |
与元素合并产生新的集合 |
++ |
与集合合并产生新的集合 |
+: |
在前面添加元素产生新的集合 |
:: |
|
:+ |
在后面添加元素产生新的集合 |
++: |
在前面添加集合产生新的集合 |
::: |
|
:++ |
在后面添加集合产生新的集合 |
- |
去除元素产生新的集合 |
-- |
去除集合产生新的集合 |
+= |
和+ 类似但修改自身而不返回新集合 |
++= |
和++ 类似但修改自身而不返回新集合 |
+=: |
和+: 类似但修改自身而不返回新集合 |
++=: |
和++: 类似但修改自身而不返回新集合 |
-= |
和- 类似但修改自身而不返回新集合 |
--= |
和-- 类似但修改自身而不返回新集合 |