Scala中的不可变数据结构

Scala中的不可变数据结构,scala,functional-programming,Scala,Functional Programming,我们知道Scala支持不可变的数据结构,即每次更新列表时,它都会在堆中创建一个新对象和引用 范例 val xs:List[Int] = List.apply(22) val newList = xs ++ (33) 因此,当我将第二个元素附加到一个列表中时,它将创建一个新的列表,其中包含22和33。 所以问题是每次我在列表中添加一个元素时,都会创建一个新的对象。我觉得这看起来效率不高。 处理此问题时是否使用了一些特殊的数据结构,如持久数据结构。有人知道这一点吗?附加到列表具有O(n)复杂度,效

我们知道Scala支持不可变的数据结构,即每次更新列表时,它都会在堆中创建一个新对象和引用

范例

val xs:List[Int] = List.apply(22)
val newList = xs ++ (33)
因此,当我将第二个元素附加到一个列表中时,它将创建一个新的列表,其中包含22和33。 所以问题是每次我在列表中添加一个元素时,都会创建一个新的对象。我觉得这看起来效率不高。 处理此问题时是否使用了一些特殊的数据结构,如持久数据结构。有人知道这一点吗?

附加到列表具有O(n)复杂度,效率低下。一种通用的方法是在构建列表时预先添加列表,最后将其反转


现在,您关于创建新对象的问题仍然适用于prepend。请注意,由于
xs
是不可变的,
newList
只指向xs作为前置后的其余数据。

虽然@manojlds的分析是正确的,但最初的帖子询问了在执行操作时复制列表节点的效率

正如@manojlds所说,构建列表通常需要逆向思考,即构建一个列表,然后逆向思考。在许多其他情况下,列表生成需要“不必要”的复制

为此,Scala中提供了一个名为
ListBuffer
的可变数据结构,您可以使用该结构构建列表,然后将结果提取为不可变列表:

val xsa = ListBuffer[Int](22)
xsa += 33
val newList = xsa.toList

但是,通常情况下,列表数据结构是不可变的,这意味着您有一些非常有用的工具来分析、分解和重新组合列表。许多内置操作利用了不变性。通过扩展,您自己的程序也可以利用这种不变性。

有充分的理由支持不变性。这些原因的一个很好的总结可以在有效的Java第15项中找到。作为性能问题的解决方案,它建议您要么依赖可变数据结构的高效内部实现,要么使用可变的对应项(例如,
ListBuffer
)。列表仅在预结束时有效。如果要追加,请使用IndexedSeq,它使用矢量数据结构。这对于追加和前置都是相当有效的,而且是不可变的。没错,但问题是它可以指向其余的数据,直到数据没有更改。例如,我有一个列表作为val list=list(12,13,14),我在不可变列表中追加一个元素,比如15,即(list1=list+15)它不会创建新列表。它将使用以前的linkedlist,指针将指向start的地址,即12,并将为15创建一个新节点。但一旦我使用list引用更改数据,它将创建一个新列表供list1使用(即复制以前的数据)并修改列表引用。这称为持久数据结构。是的,但问题是我不想在程序中引入任何可变状态。变量xsa是可变的,任何人都可以修改它的状态,这是一种危险的指示。所以我需要使用scala.collection.immutable提供的不可变数据结构。
xsa
不是一个变量。。。它的“状态”将始终是一个
ListBuffer
。随着列表的建立,列表缓冲区的内容可能会更改。然而,它只是在一段时间内,你正在建立它。一旦将其转换为列表,就不能再对其进行更改。不变性的要点是封装了可变性。