在Scala中Seq和List的区别

我在许多例子中看到,有时使用Seq,而其他时候使用List……

除了前者是Scala类型,而List来自Java之外,还有什么区别吗?

182280 次浏览

在Scala中,List继承自Seq,但实现了产品;下面是列表的正确定义:

sealed abstract class List[+A] extends AbstractSeq[A] with Product with ...

[注意:实际定义有点复杂,为了适应并利用Scala非常强大的集合框架。]

在Java术语中,Scala的Seq将是Java的List, Scala的List将是Java的LinkedList

注意,Seq是一个trait,它等价于Java的interface,但具有等价于正在兴起的防御方法。Scala的List是一个抽象类,由Nil::扩展而来,它们是List的具体实现。

因此,Java的List是一个interface,而Scala的List是一个实现。

除此之外,Scala的List是不可变的,这与LinkedList不同。事实上,Java没有与不可变集合等价的东西(只读的东西只能保证新对象不能被更改,但您仍然可以更改旧对象,因此,“只读”的对象也可以更改)。

Scala的List通过编译器和库进行了高度优化,它是函数式编程中的基本数据类型。但是,它有局限性,对于并行编程是不够的。如今,Vector是比List更好的选择,但习惯很难改掉。

Seq是序列的一个很好的泛化,所以如果你为接口编程,你应该使用它。注意,它们实际上有三个:collection.Seqcollection.mutable.Seqcollection.immutable.Seq,后者是导入作用域的“默认”。

还有GenSeqParSeq。后一种方法在可能的情况下并行运行,而前者是SeqParSeq的父方法,适用于代码并行性不重要的情况。它们都是相对较新的产品,所以人们还不怎么使用。

Seq是一个Iterable,其元素顺序已定义。序列提供了一个方法apply()用于索引,范围从0到序列的长度。Seq有很多子类,包括Queue、Range、List、Stack和LinkedList。

列表是一个实现为不可变链表的Seq。它最好用于后进先出(LIFO)访问模式的情况。

下面是来自Scala常见问题解答的完整集合类层次结构:

enter image description here

SeqList实现的trait。

如果将容器定义为Seq,则可以使用任何实现Seq特征的容器。

scala> def sumUp(s: Seq[Int]): Int = { s.sum }
sumUp: (s: Seq[Int])Int


scala> sumUp(List(1,2,3))
res41: Int = 6


scala> sumUp(Vector(1,2,3))
res42: Int = 6


scala> sumUp(Seq(1,2,3))
res44: Int = 6

请注意,

scala> val a = Seq(1,2,3)
a: Seq[Int] = List(1, 2, 3)

只是简称:

scala> val a: Seq[Int] = List(1,2,3)
a: Seq[Int] = List(1, 2, 3)

如果未指定容器类型,则底层数据结构默认为List

正如@daniel-c-sobral所说,List扩展了特征Seq,是一个由scala.collection.immutable.$colon$colon(或简称::)实现的抽象类,但抛开技术细节,请注意,我们使用的大多数列表和Seq都是以Seq(1, 2, 3)List(1, 2, 3)的形式初始化的,它们都返回scala.collection.immutable.$colon$colon,因此可以这样写:

var x: scala.collection.immutable.$colon$colon[Int] = null
x = Seq(1, 2, 3).asInstanceOf[scala.collection.immutable.$colon$colon[Int]]
x = List(1, 2, 3).asInstanceOf[scala.collection.immutable.$colon$colon[Int]]

因此,我认为唯一重要的是你想要公开的方法,例如,为了prepend,你可以使用List中的::,我发现它与Seq中的+:是多余的,我个人默认坚持使用Seq。