Follow me on GitHub

Scala | List 拼接考古

Scala 存在两个列表拼接函数::::++,初学者很容易迷惑,本文简单介绍下两者的来历。

如果读者有其他函数式编程语言基础(例如 Standard ML),肯定对用于创建 List:: 函数非常熟悉:

1
val xs = 1 :: 2 :: Nil
  • :: 是定义在 List 上的 前缀操作符,因此 1 :: Nil 实际是函数调用 Nil.::(1)
  • 而且 :: 是右结合的,因此 1 :: 2 :: Nil 实际是 Nil.::(2).::(1)

Scala 设计之初,为保持一致的函数式命名,就把 List 拼接函数命名为 :::,这样 ::::: 就对应起来了:

1
2
3
4
val xs = 1 :: 2 :: 3 :: Nil
val ys = 4 :: 5 :: Nil

val zs = xs ::: ys

:: 类似,::: 也是定义在 List 中的前缀操作符,并且也是右结合。

但时代在进步,Scala 也在发展,Scala 2.8 重新设计了其集合框架,提供 ++ 拼接两个任意 集合,自然也能用来拼接 List

::: 作为 List 拼接的经典函数,被保留下来了,因此现在 List 拼接既可以用 ::: 也可以用 ++

::::: 更符合 List 的惯用法,而 ++ 在 Scala 中更加通用。

对于 List 拼接而言,推荐使用 ::: 而非 ++,因为 :::

  • 效率更高
  • 类型安全

效率

编译器若发现 ++ 两边的参数是 List,会自动代理给 :::

x ++ yx ::: y 是没有区别的,但若是多个列表拼接,如:

1
x ++ y ++ z

因为 ++ 是左结合,因此编译器转换后变成:

1
(x ::: y) ::: z

又因为 ::: 是右结合的,所以上述代码要比 x ::: y ::: z 多执行 O(x) 步,xx 列表的长度。

类型安全

::: 两边参数必须都是 List,而 ++ 可用于任意集合,因此可用于 List 与其他集合类型拼接:

1
2
// List(1, 2, 3, a, b, c)
List(1, 2, 3) ++ "abc"

所以,如果确定是两个 List 拼接,最好用 :::