Scala 集合:基础 API
Traversable 和 Iterable 特质定义了 scala 集合的基本操作,后续文章中将要介绍的 Seq、Set,以及 Map 等集合都实现了这两个特质。本文主要对 Traversable 和 Iterable 中定义的方法进行归类和介绍,了解这些方法也就基本知道了 scala 集合的大部分操作。
Traversable 定义为 Trait 类型,包含 2 个直接派生的子特质 mutable.Traversable
和 immutable.Traversable
,分别表示可变集合和不可变集合。其中不可变集合是指集合中的元素一旦初始化完成便不可再被修改,任何对该集合的修改操作都将生成一个新的集合。Traversable 特质的定义如下:
1 | trait Traversable[+A] extends TraversableLike[A, Traversable[A]] |
Traversable 是一个 Trait 类型,所以我们不能直接通过 new 关键字来创建 Traversable 对象,但是 scala 为 Traversable 定义了伴生对象,我们可以通过伴生对象的 apply 方法创建 Traversable 类型对象(eg. Traversable(1, 2, 3)
)。同时我们可以使用 repr 函数得到这个具体的实现类对象:
1 | val t = Traversable(1 until 10: _*) |
Iterable 继承自 Traversable,也是一个特质类型,定义如下:
1 | trait Iterable[+A] extends Traversable[A] |
Iterable 同样包含 2 个直接派生的子特质 mutable.Iterable
和 immutable.Iterable
。
一. 构造 & 填充
1.1 fill
函数 fill 可以生成指定维度的集合,并使用给定的值对集合进行填充。示例:
1 | val t1 = Traversable.fill(3)("A") |
集合 t3 内容如下:
1 | List(List(18, 43, 2, 78), List(7, 78, 52, 20), List(7, 85, 77, 85)) |
函数 fill 包含多个重载版本(如下),其中第 1 组参数用于指定目标集合的维度,第 2 个函数是 elem: => A
类型,允许我们使用不同的元素对集合进行填充,例如示例中指定的随机数函数。
1 | def fill[A](n: Int)(elem: => A): CC[A] |
1.2 tabulate
函数 tabulate 与 fill 的功能类似,区别在于第 2 个函数,函数 tabulate 会将集合对应的下标值传递给函数 f,我们可以依据下标值生成集合元素值。函数 tabulate 的定义如下:
1 | def tabulate[A](n: Int)(f: Int => A): CC[A] |
示例(生成乘法口诀表):
1 | val t = Traversable.tabulate(9, 9)((x, y) => (x + 1) * (y + 1)) |
输出:
1 | 1 2 3 4 5 6 7 8 9 |
1.3 iterate
函数 iterate 的定义如下,其中参数 start 用于指定起始值,len 用于限定生成集合的长度,并依据 start 值应用 f 函数生成集合元素,生成算法为 start, f(start), f(f(start)), ...
:
1 | def iterate[A](start: A, len: Int)(f: A => A): CC[A] |
示例:
1 | val t = Traversable.iterate(1, 10)(_ + 2) |
1.4 range
函数 range 提供了 3 个参数,其中 start 和 end 用于指定结果元素值的上下界,参数 step 用于指定步进值:
1 | def range[T: Integral](start: T, end: T, step: T): CC[T] |
示例:
1 | val t = Traversable.range(0, 11, 2) |
二. 平展操作
2.1 flatten
假设我们希望对 Traversable(Traversable(1, 2), Traversable(2, 3), Traversable(3, 4))
执行平展操作得到 (1, 2, 2, 3, 3, 4)
,那么可以使用 flatten 函数实现。示例:
1 | val t = Traversable(Traversable(1, 2), Traversable(2, 3), Traversable(3, 4)) |
关于 flatten 操作的 3 个问题:
- 如果 Traversable 对象中包含的元素类型不一致怎么办,平展后的集合类型是什么?
这种情况下 scala 会从类型继承树中寻找这些类型的公共父类型,并以公共类型作为结果类型,最差的结果就是生成 Traversable[Any]
类型的集合。示例:
1 | val t = Traversable(Traversable(1, 2), Traversable(2L, 3L), Traversable("3", "4")) |
- 如果 Traversable 对象中包含的元素,有的是普通类型,有的是集合类型,能否平展?
如果不添加隐式转换,那么这种情况下是不允许平展的,因为 flatten 方法要求集合的元素必须继承或者能够转换成 GenTraversableOnce 类型。但是如果添加隐式转换,将元素类型转换成 Traversable 类型,就可以实现转换。示例:
1 | // 隐式转换 |
- 如果 Traversable 对象中包含的元素是多层嵌套集合,能否平展?
平展只能是浅层的,所以这种情况下并不会执行平展操作。
1 | val t = Traversable(Traversable(Traversable(1, 2), Traversable(2, 3)), Traversable(Traversable(3, 4))) |
平展 flatten 操作有一个比较典型的应用就是对包含 Option 类型的 Traversable 执行平展操作,剔除 None 值,返回 Some 所包含的值。示例:
1 | val t = Traversable(Some(1), None, Some(2)) |
三. 转置操作
3.1 transpose
假设我们需要一个矩阵执行转置操作(如下),那么在 scala 中可以使用 transpose 操作完成。
1 | 1 4 7 1 2 3 |
实现:
1 | val matrix = Traversable(Traversable(1, 2, 3), Traversable(4, 5, 6), Traversable(7, 8, 9)) |
注意 :每个集合中包含的元素个数必须一致。
四. (拉)拉链操作
4.1 zip
函数 zip 用于对两个 Iterable 对象执行拉拉链操作,并 以较短的集合为准,忽略较长集合中多出来的元素 ,示例:
1 | val itr1 = Iterable(1, 3, 5) |
4.2 zipAll
函数 zipAll 同样用于对两个 Iterable 对象执行拉拉链操作,但是与 zip 相反的是,函数 zipAll 以较长的集合为准,并用提供的默认值对较短的集合进行弥补 。示例:
1 | val itr1 = Iterable(1, 3, 5) |
函数 zipAll 的定义如下:
1 | def zipAll[B, A1 >: A, That](that: GenIterable[B], thisElem: A1, thatElem: B)(implicit bf: CanBuildFrom[Repr, (A1, B), That]): That |
其中参数 thisElem 用于设置左边集合对应的默认值,参数 thatElem 用于设置右边集合对应的默认值。
4.3 zipWithIndex
函数 zipWithIndex 用于将 Iterable 对象中的元素与集合下标进行拉拉链操作,示例:
1 | val itr = Iterable("A", "B", "C", "D") |
如果需要将下标放置在前面,我们可以使用 map 函数进行转换:
1 | val itr = Iterable("A", "B", "C", "D") |
五. (解)拉链操作
5.1 unzip
示例:
1 | val t = Traversable("a" -> 1, "b" -> 2, "c" -> 3) |
函数 unzip 的定义如下:
1 | def unzip[A1, A2](implicit asPair: A => (A1, A2)): (CC[A1], CC[A2]) |
函数接收一个 A => (A1, A2)
类型的 asPair 隐式参数,我们可以自定义该隐式参数,以实现更加复杂的解拉链操作,示例:
1 | val t = Traversable("a_1", "b_2", "c_3") |
5.2 unzip3
函数 unzip 用于将 1 个集合分成 2 个集合,而函数 unzip3 则用于将 1 个集合分成 3 个集合,unzip3 的定义如下:
1 | def unzip3[A1, A2, A3](implicit asTriple: A => (A1, A2, A3)): (CC[A1], CC[A2], CC[A3]) |
函数 unzip3 接收一个 A => (A1, A2, A3)
类型的 asTriple 隐式参数。示例:
1 | val t = Traversable("name, age, school", "zhenchao, 28, WHU", "guida, 28, HUST") |
六. 连接操作
6.1 ++
如果有 2 个 Traversable 对象,我们希望将这 2 个对象中的元素进行连接,可以使用 ++
函数,示例:
1 | val t1 = Traversable(1, 2, 3) |
需要注意的是,函数 ++
并不要求这两个 Traversable 对象中的元素类型必须一致,示例:
1 | val t1 = Traversable(1, 2, 3) |
连接操作的结果类型是 scala.collection.immutable.$colon$colon
,即 scala.collection.immutable.::
,其中 ::
类型是 List 的子类型。具体的元素类型是两个 Traversable 对象中元素类型的公共父类型,这里对应的是 Any 类型。
在 Traversable 的实现中,结果类型与左边的集合类型保持一致,示例:
1 | val t1 = List(1, 2, 3) |
另外 Traversable 还提供了 ++:
方法,该方法也表示连接操作,只是将左边的集合连接到右边的集合,结果类型由右边的集合决定。 在 scala 中,以 :
结尾的函数都是右操作的 ,即 A ++ B
等价于 B ++: A
。
Scala 允许以一些特殊符号对类或方法进行命名,但是这在 JVM 中是不允许,为了保证能够正常编译,Scala 使用 mangled 技术将这些特殊字符编码成 $name
的形式以满足 JVM 的要求。对应字符编码之后的值如下:
1 | ~ -> $tilde |
6.2 concat
如果我们需要对多个 Traversable 对象执行连接操作,一种解决方式就是对所有的对象执行 ++
操作,即 A ++ B ++ C ++ ...
,但是这样会在每次执行 ++
操作时生成一个新的集合,影响性能。
这种情况下可以使用 concat 函数,它会预先计算出所需的结果集合大小,然后生成结果,减少了中间临时集合对象的生成。示例:
1 | val t = Traversable.concat(Traversable(0 to 5: _*), Traversable(5 to 10: _*), Traversable(10 to 15: _*)) |
七. 使用偏函数(PartialFunction)对结果进行收集
7.1 collect & collectFirst
函数 collect 的定义如下,它接收一个偏函数 PartialFunction 类型的参数:
1 | def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[Repr, B, That]): That |
我们可以自定义偏函数对 Traversable 集合中的元素进行筛选,仅保留满足条件的集合,示例:
1 | def filterEven: PartialFunction[Int, String] = { |
与 filter 函数不同的是,偏函数接收一个集合中的元素 A,并输出一个结果元素 B,元素 B 的类型可以与 A 的类型不同。可以说 collect 函数兼具 filter 和 map 的功能。
函数 collectFirst 是 collect 的特殊版本,它返回满足条件的第 1 个元素,对应 Option 类型,如果不存在满足条件的元素则返回 None。
偏函数说明:
包括在花括号内的一组 case 语句组成一个偏函数(partial function),偏函数并非对所有输入值都有定义,常见的 try-catch 语句的 catch 子句就是一个偏函数。偏函数是特质 PartialFunction[-A, +B]
的一个实例,其中 A 是入参类型,B 是返回值类型,该类有两个方法,apply 方法从匹配到的模式计算函数值,isDefinedAt 方法校验当前的输入是否有对应匹配的模式,如果有则返回 true,否则返回 false。示例:
1 | val f: PartialFunction[Char, Int] = { |
调用:
1 | "-3+4".collect(f) // Vector(-1, 1) |
前面说到偏函数并非对所有的输入值都有定义,这里我们的入参为 -3+4
,而偏函数 f 仅对 -+
有定义,所以返回值是 (-1, 1)
。如果完全覆盖了所有场景则是一个 Function1,而不仅仅是一个 PartialFunction。
我们可以调用 PartialFunction#lift
方法将一个偏函数转换成返回 Option[R]
类型的常规函数,这样对于偏函数有定义的输入值返回 Some 类型,没有的则返回 None。方法 unlift 可以将一个常规函数转换成一个偏函数。
八. 过滤操作
8.1 filter & filterNot
函数 filter 和 filterNot 用于对 Traversable 对象中的元素进行筛选,区别在于前者保留满足筛选条件的元素,而后者保留不满足筛选条件的元素。示例:
1 | val t = Traversable(1 to 10: _*) |
8.2 withFilter
函数 withFilter 同样接收一个谓词 A => Boolean
,对 Traversable 对象中的元素进行筛选,并保留满足条件的元素。区别于 filter,函数 withFilter 返回的结果类型是 FilterMonadic 特质,定义如下:
1 | trait FilterMonadic[+A, +Repr] extends Any { |
也就是说我们在调用 withFilter 函数之后,接下去只能调用 map、flatMap、foreach,以及 withFilter 这几个函数。这里对应函数式编程的 Monad 概念,表示一个计算序列,可以让程序使用管道式的方式处理数据。在这样的计算模式中可以流式调用多个上述函数,但是只有在调用 foreach 时才会真正执行计算。而 filter 函数在每次调用时都会进行计算并返回一个新的集合,因此 withFilter 在性能上会更加高一些。示例:
1 | val t = Traversable(1 to 10: _*) |
九. 归约操作
9.1 scan & scanLeft
假设我们希望计算 [1, 5] 区间数据的阶乘,最简单的方式就是定义一个计算阶乘的函数,然后遍历应用集合中的每个元素,但是这样每次都需要从 1 开始执行计算,而不能复用之前的计算结果,实际上 5! = 5 * 4!
,我们计算完 4 的阶乘之后乘以 5 即得到 5 的阶乘,而不需要重 1 开始重新计算。
使用 scan 函数我们可以做到复用,函数 scan 的定义如下,它接收一个初始值 z 和一个操作符 op, 前一次的计算结果会作为初始值传递给下一次计算 :
1 | def scan[B >: A, That](z: B)(op: (B, B) => B)(implicit cbf: CanBuildFrom[Repr, B, That]): That |
计算阶乘的示例:
1 | val t = Traversable(1 to 5: _*) |
函数 scan 只是 scanLeft 的别名,本质上就是 scanLeft。
9.2 scanRight
函数 scan 从左往右对集合进行遍历,而 scanRight 则从右往左对集合进行遍历,示例:
1 | val t = Traversable(1 to 5: _*) |
函数 scanRight 会从右往左对集合中的元素进行遍历,并将计算结果按照同样的顺序记录到结果集合中,同时将中间结果传递给下一次计算作为初始值。
9.3 fold & foldLeft
函数 fold 的作用与 scan 有些相似,会将上一次计算得到的中间结果传递给下一次计算,但是区别于 scan,函数 fold 并不会输出中间结果,而只是返回函数最后一次计算得到的最终结果。
示例:
1 | val t = Traversable(1 to 10: _*) |
上述示例使用 fold 对集合中的元素进行求和,本质上与 sum 函数是一致的,实际上 sum 底层也是依赖于 fold 实现的。
函数 fold 只是 foldLeft 的别名,本质上就是 foldLeft。
Scala 为 foldLeft 提供了简写版的 /:
函数,上面的示例可以改写如下:
1 | val sum = (0 /: t) (_ + _) |
9.4 foldRight
函数 foldRight 用于对集合中元素从右往左进行遍历,并应用计算,示例:
1 | val t = Traversable("a", "b", "c") |
Scala 也为 foldRight 提供了简写版的 :\
函数,上面的示例可以改写如下:
1 | val res = (t :\ "x") (_ + _) |
9.5 reduce & reduceOption & reduceLeft & reduceLeftOption
函数 reduce 在功能上与 fold 相同,只是不需要提供初始值,函数 reduce 会以集合的第 1 个元素作为初始值,并提供从左往右的归约计算。示例:
1 | val t = Traversable(1 to 10: _*) |
其实函数 reduce 和 reduceOption 分别对应函数 reduceLeft 和 reduceLeftOption 的别名,二者的区别在于当集合为空时,reduce 会抛出异常,而 reduceOption 只是返回 None。如果集合中只有 1 个元素,那么两个函数均返回该元素,而不是抛出异常。
9.6 reduceRight & reduceRightOption
函数 reduceRight 对标 reduceLeft,函数 reduceRightOption 对标 reduceLeftOption,区别仅在于是从右往左进行计算,示例:
1 | val t = Traversable(1 to 10: _*) |
十. 元素获取与检索
10.1 head & headOption
函数 head 和 headOption 都是用于从 Traversable 对象中获取第一个元素,区别在于前者在元素不存在时抛出 NoSuchElementException 异常,而后者返回 None。示例:
1 | val t = Traversable(1 to 10: _*) |
10.2 last & lastOption
函数 last 和 lastOption 都是用于从 Traversable 对象中获取最后一个元素,区别在于前者在元素不存在时抛出 NoSuchElementException 异常,而后者返回 None。示例:
1 | val t = Traversable(1 to 10: _*) |
注意 :对于 Traversable 来说,默认的 last 实现会遍历整个集合,时间复杂度为 O(n)
。
10.3 find
函数 find 用于从 Traversable 对象中基于给定的筛选条件 A => Boolean
选择第一个满足条件的元素,如果不存在则返回 None。示例:
1 | val t = Traversable(1 to 10: _*) |
10.4 tail & tails
前面介绍了 head 函数用于返回 Traversable 对象的第一个元素,而 tail 函数正好与 head 函数互补,用于返回 Traversable 对象除第一个元素以外的剩余元素。示例:
1 | val t = Traversable(1 to 10: _*) |
我们可以说一个集合是由 head 和 tail 组成的:head :: tail
。
函数 tails 与 tail 的作用类似,但是多了一个 s,所以该函数的返回结果是一个集合,可以将 tails 看做是对集合迭代执行 tail 操作并生成结果集,其中第一个结果是原集合,而最后一个结果是空集合。示例:
1 | val t = Traversable(1 to 10: _*) |
输出:
1 | List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) |
10.5 init & inits
函数 init 的作用正好与 tail 相反,它与 last 函数正好互补,用于返回 Traversable 对象除最后一个元素以外的剩余元素。示例:
1 | val t = Traversable(1 to 10: _*) |
而 inits 函数的作用也正好与 tails 相反,示例:
1 | val t = Traversable(1 to 10: _*) |
输出:
1 | List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) |
10.6 take & takeWhile
函数 take 用于从 Traversable 中获取前 n 个元素(如果集合长度小于 n,则返回全部元素),示例:
1 | val t = Traversable(1, 2, 3, 4, 5, 4, 3, 2, 1) |
函数 takeWhile 接收一个谓词 A => Boolean
,用于从左往右对 Traversable 对象中的元素进行筛选,直到第一个不满足条件的元素为止。示例:
1 | val t = Traversable(1, 2, 3, 4, 5, 4, 3, 2, 1) |
10.7 drop & dropWhile
函数 drop 与 take 刚好相反,用于获取 Traversable 对象中除前 n 个元素之外的元素(如果集合长度小于 n,则返回空集合),示例:
1 | val t = Traversable(1, 2, 3, 4, 5, 4, 3, 2, 1) |
如果 n 小于等于 0,则返回整个集合。
函数 dropWhile 与 takeWhile 刚好相反,它也接收一个谓词 A => Boolean
,用于从左往右对 Traversable 对象中的元素进行筛选并跳过开头连续满足谓词的的元素,并返回该元素之后元素组成的集合。示例:
1 | val t = Traversable(1, 2, 3, 4, 5, 4, 3, 2, 1) |
10.8 takeRight & dropRight
函数 takeRight 用于获取 Iterable 集合的后 n 个元素,而函数 dropRight 用于阶段 Iterable 集合的后 n 个元素,示例:
1 | val itr = Iterable(1 to 9: _*) |
10.9 slice
函数 slice 用于获取原 Traversable 对象的一个子集合,函数的定义为 slice(from: Int, until: Int)
,第 2 个参数命名为 until,所以我们可以知道截取的是一个 左闭右开 的区间。示例:
1 | val t = Traversable(1 to 10: _*) |
注意 :如果 from 或 until 的参数值超过了集合的上下标,则以集合的上下标为准,不会抛出异常。
十一. 分组操作
11.1 splitAt
函数 splitAt 接收一个参数 n,并以位置 n 将 Traversable 集合分割成前后两部分,示例:
1 | val t = Traversable(1 to 10: _*) |
功能上类似于 (t.take(n), t.drop(n))
。
11.2 span
函数 span 接收一个谓词 A => Boolean
,并且从左往右对 Traversable 集合进行遍历,将开头连续满足条件的元素分为一组,剩下的元素分为一组。示例:
1 | val t = Traversable(1 to 10: _*) |
功能上类似于 (t.takeWhile(n), t.dropWhile(n))
。
11.3 partition
函数 partition 接收一个谓词 A => Boolean
,相对于 span 的区别在于它会对集合中所有的元素进行筛选,并将满足条件的元素分为一组,不满足条件的元素分为另一组。示例:
1 | val t = Traversable(1 to 10: _*) |
11.4 groupBy
函数 groupBy 接收一个 A => K
类型参数,对 Traversable 对象中的元素进行计算并得到一个 K 类型的值,然后以 K 值作为 key,对应的集合元素作为 value,构建 Map 结果集。示例:
1 | val t = Traversable(1 to 10: _*) |
输出:
1 | (2,List(2, 5, 8)) |
11.5 grouped
函数 grouped 用于对 Iterable 对象中的元素进行分组,该函数接收一个 size 参数,用于将原集合分组成长度为指定大小的多个子集合,对于最后一个子集合,其长度可能小于 size。示例:
1 | val itr = Iterable(1 to 9: _*) |
输出:
1 | List(1, 2, 3, 4) |
11.6 sliding
函数 sliding 用于对 Iterable 对象进行窗口操作,该函数定义如下,其中参数 size 用于指定窗口的大小,而参数 step 用于指定每次滑动的步长(默认为 1):
1 | def sliding(size: Int): Iterator[Repr] |
示例:
1 | val itr = Iterable(1 to 9: _*) |
输出:
1 | List(1, 2, 3) |
十二. 检查操作
12.1 forall & exist
函数 forall 和 exist 都接收一个谓词 A => Boolean
,用于对集合中的元素进行检查,区别在于前者会对所有的元素进行校验,并在所有元素都满足条件时返回 true,而后者只需要有一个元素满足条件即返回 true。示例:
1 | val t = Traversable(1 to 10: _*) |
注意 :对于一个空集合,函数 forall 会返回 true。
12.2 count
函数 count 接收一个谓词 A => Boolean
,该函数会对 Traversable 对象中所有的元素进行检查,并返回满足条件的元素个数,示例:
1 | val t = Traversable(1 to 10: _*) |
注意 :不推荐使用 t.filter(_ % 2 == 0).size
进行计算,因为这样会生成一个中间集合,性能较差。
十三. 聚合操作
聚合操作对集合中的元素执行计算,并返回单一的值。
13.1 sum & product
函数 sum 和 product 分别用于求解集合中元素的 和 与 积 ,示例:
1 | val t = Traversable(1 to 10: _*) |
13.2 min & max
函数 min 和 max 分别用于求解集合中元素的最小值和最大值,示例:
1 | val t = Traversable(1 to 10: _*) |
其中 min 和 max 函数的定义如下:
1 | min[B >: A](implicit cmp: Ordering[B]): A |
它们都接受一个隐式参数 cmp,我们可以利用该参数自定义比较器。
13.3 minBy & maxBy
函数 min 和 max 都是依据集合中元素值本身进行比较,而函数 minBy 和 maxBy 则允许我们指定比较的因子,示例:
1 | val t = Traversable("111", "2", "33") |
13.4 aggregate
函数 aggregate 是一个比 fold 和 reduce 更加抽象的高阶函数,应用上更加灵活,该函数不要求输出的结果必须是集合元素类型的父类型。函数 aggregate 定义如下:
1 | aggregate[B](z: =>B)(seqop: (B, A) => B, combop: (B, B) => B): B |
其中 z 是初始值,seqop 用于在遍历分区的时候更新结果,combop 用于汇总各个分区的结果。
示例:
1 | val t = Traversable("111", "2", "33") |
上述示例将集合中的每个元素都转换成 Int 类型,并使用 seqop 执行求和操作,其中初始值 z 设置为 0。这里因为没有启用并行计算,所以只有一个分组在运行,对应的 combop 没有意义,我们可以将所有分区的求和结果置为 0,即 t.aggregate(0)(_ + _.toInt, (_, _) => 0)
,对应的结果不会变化。但是如果我们启用并行计算,即 t.par.aggregate(0)(_ + _.toInt, (_, _) => 0)
,那么结果就会是 0,改为 t.par.aggregate(0)(_ + _.toInt, _ + _)
即能拿到正确结果。
十四. 生成字符串
14.1 mkString & addString
函数 mkString 用于对 Traversable 对象中的元素拼接生成字符串,并且允许指定元素的分隔符,以及前缀和后缀。示例:
1 | val t = Traversable(1 until 10: _*) |
函数 addString 同样用于对 Traversable 对象中的元素拼接生成字符串, 相对于 mkString 的区别在于需要提供 StringBuilder 对象 ,同样允许指定分隔符、前缀和后缀。 事实上 mkString 就是利用 addString 实现的 。示例:
1 | val t = Traversable(1 until 10: _*) |
14.2 stringPrefix
函数 stringPrefix 用于返回集合对象的实际类型名称,示例:
1 | val t = Traversable(1 until 10: _*) |
十五. 复制元素到数组
15.1 copyToArray & copyToBuffer
函数 toArray 可以将一个 Traversable 对象转换成一个数组对象,如果我们希望将 Traversable 对象中已有的元素复制到一个已有的数组中,那么可以使用 copyToArray 函数。示例:
1 | val t = Traversable(1 until 10: _*) |
函数 copyToArray 包含 3 个重载版本,如下:
1 | copyToArray[B >: A](xs: Array[B]): Unit |
其中参数 start 对应目标数组的下标,表示待复制的元素将写入数组的哪个位置,默认为 0,参数 len 表示要复制的元素长度,默认为集合的长度,如果 len 超过了集合的长度,则以集合长度为准。
函数 copyToBuffer 用于将元素复制到所提供的 buffer 对象中,示例:
1 | val t = Traversable(1 until 10: _*) |
函数 copyToBuffer 没有提供其它重载版本。
十六. 生成视图
16.1 view
函数 view 用于生成 Traversable 对象的视图(相当于对原集合对象的引用),它接收两个参数 from 和 until,用于指定目标视图的生成区间,如果不指定这 2 个参数则创建整个 Traversable 对象中元素的视图。示例:
1 | val t = mutable.Seq(1 until 10: _*) |
函数 view 与 slice 的区别 :
函数 view 生成集合的一个非严格模式(non-strict)视图,即 view 是延迟计算的,如果希望转换成严格模式,则可以调用视图的 force 函数,非严格模式的视图可以看做是对原集合区间的一个引用,当原集合区间中的元素发生变更时,会反应到视图上。