关于scala的集合

默认集合

我们通过基础集合类的伴生对象也可创建出默认的具体集合实例,那么,默认的集合类型是哪种呢?在REPL里验证如下:

Traversable

Traversable(1,2)
res0: Traversable[Int] = List(1, 2)
Traversable需支持foreach接口,返回List。

Iterable

Iterable(1,2)
res1: Iterable[Int] = List(1, 2)
Iterable需支持返回外部迭代器(Iterator),返回List。

Seq

Seq(1,2)
res2: Seq[Int] = List(1, 2)
Seq的默认实现是List,List是链表。
【注意】虽然Seq要支持
def length: Int
def apply(idx: Int): A
两个接口,但List的length和apply操作都不够高效,需要通过逐一遍历链表元素得到结果。若遇到要使用length和apply两个接口的场景,应改为使用IndexedSeq或直接用Vector

IndexedSeq

IndexedSeq(1,2)
res3: IndexedSeq[Int] = Vector(1, 2)
IndexedSeq的默认类型是Vector很好理解,因为它要高效的支持
def length: Int
def apply(idx: Int): A
两个接口,而Vector是能做到高效的随机访问及长度计算的。

LinearSeq

LinearSeq(1,2)
REPL不支持直接写,但在代码里可以这样写,返回类型也是List。

Buffer

mutable.Buffer(1,3)
REPL不支持直接写,但在代码里可以这样写,返回类型是mutable.ArrayBuffer。

Set

Set(1,1,2)
res5: scala.collection.immutable.Set[Int] = Set(1, 2)
返回不可变set

Map

Map(1->”china”, 2->”russia”)
res6: scala.collection.immutable.Map[Int,String] = Map(1 -> china, 2 -> russia)
返回不可变map

Iterator注意事项

Iterator继承自TraversableOnce,亦即只能遍历一次,所以在调用Iterator的length或遍历完Iterator之后,再访问就只能得到empty iterator了,见下例:

val l = Iterator(1,2,3)
Assert.assertEquals(l.length, 3)
Assert.assertEquals(l.length, 0)

所以在定义常量集合的时候,千万不要使用Iterator,一般的,如果只有遍历诉求,定义成Traversable(1,2,3)即可。

可变集合

在直接使用可变集合时,不妨问一下自己:我是否真的需要可变集合,我能否通过map、flatmap等集合操作或for工作流来直接得到一个结果集合
从函数式编程的观点看,可变集合应该是要尽量避免的。只有想不出更好的办法,我才会用可变集合。
回到可变集合本身,我们以Buffer为例来说明,它有两个子类:ArrayBuffer和ListBuffer。

ArrayBuffer顾名思义,底层就是一个Array,在做+=操作时,会自动扩展Array的空间并做旧数据的拷贝,类似于java的ArrayList和C++的std::vector,所以,ArrayBuffer的length和apply操作的效率很高。另外,同java的ArrayList一样,为效率计,最好为ArrayBuffer赋予初始容量,例如:
val l = new ArrayBuffer[String](10)

ListBuffer底层正是一个List,所以其+=操作耗费常量时间。apply操作则使用的是底层List的apply,所以效率不高。由于ListBuffer内置了记录长度的成员变量,所以length耗时也是常量。ListBuffer类似于java的LinkedList和C++的std::list。

集合join为字符串

注意,不要使用集合的reduce,由于string的不变性,reduce效率很差。事实上,每个集合都有一个mkString操作,可实现join能力,其底层实现用的是StringBuilder。顺带说一下,java下8之前我们会用guava的Joiner类,8及之后则可使用Collectors.joining。

猜你喜欢

转载自blog.csdn.net/tlxamulet/article/details/78138169