Scala中Seq和List的区别

379

我在很多例子中看到有时使用Seq,而有时则使用List...

除了前者是Scala类型而List来自Java之外,它们是否有任何区别?

5个回答

496
在Java术语中,Scala的Seq相当于Java的List,Scala的List相当于Java的LinkedList
需要注意的是,Seq是一个trait,类似于Java的interface,但具有即将推出的defender方法的等效。Scala的List是一个抽象类,由Nil::扩展,它们是List的具体实现。
因此,在Java中,List是一个接口,而在Scala中,List是一个实现。
此外,Scala的List是不可变的,而LinkedList则不是。事实上,Java没有不可变集合的等价物(只读保证新对象不能被更改,但您仍然可以更改旧对象,因此“只读”也会被更改)。
Scala的List经过编译器和库高度优化,是函数式编程中的基本数据类型。然而,它存在一些限制,对于并行编程来说是不足的。这些天,VectorList更好,但习惯难以改变。
Seq是序列的很好的概括,因此如果您按照接口进行编程,应该使用它。需要注意的是,实际上有三个:collection.Seqcollection.mutable.Seqcollection.immutable.Seq,后者是默认导入到作用域中的。
还有GenSeqParSeq。后者在可能的情况下并行运行,而前者是SeqParSeq的父类,适用于没有对代码并行性的担忧时进行概括。它们都比较新,所以人们使用它们的次数不多。

3
“Java没有不可变集合的等价物”虽然String不是一个集合,但它是Java程序员熟悉的不可变类的例子。 - huynhjl
19
@huynhjl 那不是重点。我只是在Java和Scala之间进行比较,而在Java中并没有可变/不可变集合的概念。 - Daniel C. Sobral
2
Java实际上有不可变集合的等效物。虽然它没有被“广泛宣传”,但是当您大量使用泛型时,由于这个原因,您可能会遇到一些UnsupportedOperationException。在Java中创建不可变列表,您可以使用Collections.unmodifiableList(),类似地,还有其他方法用于Sets、Maps等。http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#unmodifiableList(java.util.List) - jbx
32
@jbx 不正确。如果您使用这些方法,则会得到一个对象,该对象将在修改它的方法上抛出异常,但不是一个_不可变_对象。如果在创建不可修改对象之后修改了原始对象,则不可修改对象将反映这一点。因此,是不可修改的,但不是完全不可变的。 - Daniel C. Sobral
4
@jbx 接收方法不能保持对其所接收的集合的引用并假定它永远不会更改,标准Java库中也没有类型能够保证这一点--即不可变性。因此,例如,该接收方法无法保证线程安全性。而且,这还没有涉及到由不可变性启用的持久特性。如果没有所有这些,就不能称其为“等效”。 - Daniel C. Sobral
显示剩余11条评论

130

Seq(序列)是具有定义顺序的元素的可迭代对象。序列提供了一个用于索引的apply()方法,范围从0到序列长度。Seq有许多子类,包括Queue(队列)、Range(范围)、List(列表)、Stack(栈)和LinkedList(链表)。

List(列表)是作为不可变链接列表实现的Seq。它最适合在后进先出(LIFO)的访问模式下使用。

这是来自Scala FAQ的完整集合类层次结构:

enter image description here


5
数组(Array)和数组缓冲区(ArrayBuffer)都不属于可迭代对象的范畴。请问还有其他需要翻译的内容吗? - Peter Krauss
1
此答案展示了如何在数组中查找元素的索引。 - Topa
如果你对于“肯定的”感到好奇,最好将其用于后进先出(LIFO)访问模式的情况下。可以考虑使用prepend()head()操作,其时间复杂度为O(1),而不是append()last()操作,后者的时间复杂度为列表中元素的数量O(n)。 - Ricardo

36

SeqList 实现的一个 trait。

如果你将你的容器定义为 Seq,你可以使用任何实现了 Seq trait 的容器。

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


18
在Scala中,List继承自Seq,但实现了Product;这里是List的正确定义:
sealed abstract class List[+A] extends AbstractSeq[A] with Product with ...

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

2
最初的回答已经提到,List继承了Seq特质并由scala.collection.immutable.$colon$colon(或简写为::)实现了抽象类。但是,技术细节放一边,需要注意的是我们使用的大多数列表和序列都是以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]]

作为结果,我认为唯一重要的是您想要公开的方法,例如要添加元素,您可以使用List中的::,但我发现与Seq中的+:重复,因此我个人默认使用Seq。"最初的回答"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接