Scala Seq(序列数组)

2019-05-12 19:23:09 | 编辑 | 添加

Scala 中 Seq 列表和Array数组是差不多的意思。数组是一种特殊的collection,Seq是列表,适合存有序重复数据,进行快速插入/删除元素等场景,Set是集合,适合存无序非重复数据,进行快速查找海量元素的等场景.Seq 和 Set是针对现实使用场景的不同数据结构抽象。


Scala 2.8中数组不再看作序列,因为本地数组的类型不是Seq的子类型。而是在数组和 scala.collection.mutable.WrappedArray这个类的实例之间隐式转换,后者则是Seq的子类


scala> val seq: Seq[Int] = a1
seq: Seq[Int] = WrappedArray(1, 2, 3)
scala> val a4: Array[Int] = s.toArray
a4: Array[Int] = Array(1, 2, 3)
scala> a1 eq a4
res1: Boolean = true

上面的例子说明数组与序列是兼容的,因为数组可以隐式转换为WrappedArray。反之可以使用Traversable提供的toArray方法将WrappedArray转换为数组。



数组还有另外一种隐式转换,不需要将数组转换成序列,而是简单地把所有序列的方法“添加”给数组。“添加”其实是将数组封装到一个ArrayOps类型的对象中,后者支持所有序列的方法。ArrayOps对象的生命周期通常很短暂,不调用序列方法的时候基本不会用到,其内存也可以回收。现代虚拟机一般不会创建这个对象。


在接下来REPL中展示数组的这两种隐式转换的区别:

scala> val seq: Seq[Int] = a1
seq: Seq[Int] = WrappedArray(1, 2, 3)
scala> seq.reverse
res2: Seq[Int] = WrappedArray(3, 2, 1)
scala> val ops: collection.mutable.ArrayOps[Int] = a1
ops: scala.collection.mutable.ArrayOps[Int] = [I(1, 2, 3)
scala> ops.reverse
res3: Array[Int] = Array(3, 2, 1)


注意seq是一个WrappedArray,seq调用reverse方法也会得到一个WrappedArray。这是没问题的,因为封装的数组就是Seq,在任意Seq上调用reverse方法都会得到Seq。反之,变量ops属于ArrayOps这个类,对其调用reverse方法得到一个数组,而不是Seq。


上例直接使用ArrayOps仅为了展示其与WrappedArray的区别,这种用法非常不自然。一般情况下永远不要实例化一个ArrayOps,而是在数组上调用Seq的方法:


scala> a1.reverse
res4: Array[Int] = Array(3, 2, 1)

ArrayOps的对象会通过隐式转换自动的插入,因此上述的代码等价于


scala> intArrayOps(a1).reverse
res5: Array[Int] = Array(3, 2, 1)


这里的intArrayOps就是之前例子中插入的隐式转换。这里引出一个疑问,上面代码中,编译器为何选择了intArrayOps而不是WrappedArray做隐式转换?毕竟,两种转换都是将数组映射到支持reverse方法的类型,并且指定输入。答案是两种转换是有优先级次序的,ArrayOps转换比WrappedArray有更高的优先级。前者定义在Predef对象中,而后者定义在继承自Predef的scala.LowPriorityImplicits类中。子类、子对象中隐式转换的优先级低于基类。所以如果两种转换都可用,Predef中的会优先选取。字符串的情况也是如此。


数组与序列兼容,并支持所有序列操作的方法,你现在应该已经了然于胸。


这里就不再继续讲解Seq的操作符和使用方法案例,上面讲解过Set的使用方法,其实集合的操作都是差不多的,你只需要记得每种集合的特性,有序还是无序,存储结构等。