对scala中的集合类虽然有使用,可是一直处于只知其一;不知其二的状态。尤为是与java中各类集合类的混合使用,虽然用过不少次,可是一直也没有作比较深刻的了解与分析。正好趁着最近项目的须要,加上稍微有点时间,特地多花了一点时间对scala中的集合类作个详细的总结。php
在说集合类以前,先看看scala中的数组。与Java中不一样的是,Scala中没有数组这一种类型。在Scala中,Array类的功能就与数组相似。
与全部数组同样,Array的长度不可变,里面的数据能够按索引位置访问。java
def test() = {
val array1 = new Array[Int](5)
array1(1) = 1
println(array1(1))
val array2 = Array(0, 1, 2, 3, 4)
println(array2(3))
}
上面的demo就演示了Array的简单用法。web
盗用网上的一张图,scala中集合类的大致框架以下图所示。
编程
特地查了下scala的源码,贴上几张图,能够对应到上面的这幅继承关系图。
数组
根据图以及源码能够很清晰地看出scala中的集合类能够分为三大类:
1.Seq,是一组有序的元素。
2.Set,是一组没有重复元素的集合。
3.Map,是一组k-v对。数据结构
Seq主要由两部分组成:IndexedSeq与LinearSeq。如今咱们简单看下这两种类型。app
首先看IndexedSeq,很容易看出来这种类型的主要访问方式是经过索引,默认的实现方式为vector。框架
def test() = {
val x = IndexedSeq(1,2,3)
println(x.getClass)
println(x(0))
val y = Range(1, 5)
println(y)
}
将以上函数运行起来之后,输出以下:编程语言
class scala.collection.immutable.Vector
1
Range(1, 2, 3, 4)
而做为LinearSeq,主要的区别在于其被分为头与尾两部分。其中,头是容器内的第一个元素,尾是除了头元素之外剩余的其余全部元素。LinearSeq默认的实现是List。svg
def test() = {
val x = collection.immutable.LinearSeq("a", "b", "c")
val head = x.head
println(s"head is: $head")
val y = x.tail
println(s"tail of y is: $y")
}
将上面的代码运行起来之后,获得的结果以下:
head is: a
tail of y is: List(b, c)
与其余任何一种编程语言同样,Scala中的Set集合类具备以下特色:
1.不存在有重复的元素。
2.集合中的元素是无序的。换句话说,不能以索引的方式访问集合中的元素。
3.判断某一个元素在集合中比Seq类型的集合要快。
Scala中的集合分为可变与不可变两种,对于Set类型天然也是如此。先来看看示例代码:
def test() = {
val x = immutable.HashSet[String]("a","c","b")
//x.add("d")没法使用,由于是不可变集合,没有add方法。
val y = x + "d" + "f" // 增长新的元素,生成一个新的集合
val z = y - "a" // 删除一个元素,生成一个新的集合
val a = Set(1,2,3)
val b = Set(1,4,5)
val c = a ++ b // 生成一个新的集合,增长集合
val d = a -- b // 生成一个新的集合,去除集合
val e = a & b // 与操做
val f = a | b // 或操做
}
由于上面代码里的集合类型都是不可变类型,因此全部语句结果其实都是生成一个新的集合。
def test() = {
val x = new mutable.HashSet[String]()
x += "a" // 添加一个新的元素。注意此时没有生成一个新的集合
x.add("d") //由于是可变集合,因此有add方法
x ++= Set("b", "c") // 添加一个新的集合
x.foreach(each => println(each))
x -= "b" // 删除一个元素
println()
x.foreach(each => println(each))
println()
val flag = x.contains("a") // 是否包含元素
println(flag)
}
将上面这段代码运行起来之后,获得的结果以下:
c
d
a
b
c
d
a
true
Map这种数据结构是平常开发中使用很是频繁的一种数据结构。Map做为一个存储键值对的容器(key-value),其中key值必须是惟一的。 默认状况下,咱们能够经过Map直接建立一个不可变的Map容器对象,这时候容器中的内容是不能改变的。示例代码以下。
def test() = {
val peoples = Map("john" -> 19, "Tracy" -> 18, "Lily" -> 20) //不可变
// people.put("lucy",15) 会出错,由于是不可变集合。
//遍历方式1
for(p <- peoples) {
print(p + " ") // (john,19) (Tracy,18) (Lily,20)
}
//遍历方式2
peoples.foreach(x => {val (k, v) = x; print(k + ":" + v + " ")}) //john:19 Tracy:18 Lily:20
//遍历方式3
peoples.foreach ({ case(k, v) => print(s"key: $k, value: $v ")})
//key: john, value: 19 key: Tracy, value: 18 key: Lily, value: 20
}
上面代码中的hashMap是不可变类型。
若是要使用可变类型的map,可使用mutable包中的map相关类。
def test() = {
val map = new mutable.HashMap[String, Int]()
map.put("john", 19) // 由于是可变集合,因此能够put
map.put("Tracy", 18)
map.contains("Lily") //false
val res = getSome(map.get("john"))
println(res) //Some(19)
}
def getSome(x:Option[Int]) : Any = {
x match {
case Some(s) => s
case None => "None"
}
}
特地将ArrayBuffer单独拎出来,是由于ArrayBuffer相似于Java中的ArrayList。而ArrayList在Java中是用得很是多的一种集合类。
ArrayBuffer与ArrayList不同的地方在于,ArrayBuffer的长度是可变的。与Array同样,元素有前后之分,能够重复,能够随机访问,可是插入的效率不高。
def test() = {
val arrayBuffer = new mutable.ArrayBuffer[Int]()
arrayBuffer.append(1) //后面添加元素
arrayBuffer.append(2)
arrayBuffer += 3 //后面添加元素
4 +=: arrayBuffer //前面添加元素
}
scala最大的优点之一就是可使用JDK上面的海量类库。实际项目中,常常须要在java集合类与scala集合类之间作转化。具体的转换对应关系以下:
scala.collection.Iterable <=> Java.lang.Iterable
scala.collection.Iterable <=> Java.util.Collection
scala.collection.Iterator <=> java.util.{ Iterator, Enumeration }
scala.collection.mutable.Buffer <=> java.util.List
scala.collection.mutable.Set <=> java.util.Set
scala.collection.mutable.Map <=> java.util.{ Map, Dictionary }
scala.collection.mutable.ConcurrentMap <=> java.util.concurrent.ConcurrentMap
scala.collection.Seq => java.util.List
scala.collection.mutable.Seq => java.util.List
scala.collection.Set => java.util.Set
scala.collection.Map => java.util.Map
java.util.Properties => scala.collection.mutable.Map[String, String]
在使用这些转换的时候,只须要scala文件中引入scala.collection.JavaConversions._ 便可。
通常比较多件的场景是在scala中调用java方法。如前面所讲,jdk的类库太丰富了,在scala中会常常有调用java方法的需求。给个简单的例子:
假设有以下java代码:
public class TestForScala {
public static <T> void printCollection(List<T> list) {
for(T t: list) {
System.out.println(t);
}
}
}
咱们想在scala代码中调用TestForScala类中的printCollection方法。能够这么写:
def test() = {
val raw = Vector(1, 2, 3)
TestForScala.printCollection(raw)
}
java方法中须要的参数是个List,参照咱们前面的转换关系,scala.collection.Seq能够自动转化为java中的List,而Vector就是scala中Seq的实现,因此能够直接传入到printCollection方法中!