Scala有各种各样的不可变序列,如List,Vector等。我很惊讶地发现没有一个简单数组支持的不可变索引序列的实现(Vector似乎对我的需求太复杂了。)
这有设计理由吗?我在邮件列表上找不到一个好的解释。
您是否建议使用与阵列具有接近相同性能的不可变索引序列?我正在考虑scalaz的ImmutableArray,但它有一些scala trunk的问题。
谢谢
答案 0 :(得分:31)
您可以将数组转换为序列。
val s: Seq[Int] = Array(1,2,3,4)
数组将隐式转换为WrappedArray。由于类型是Seq,更新操作将不再可用。
答案 1 :(得分:19)
所以,让我们首先区分界面和类。接口是API设计,而类是这种API的实现。
Scala中的接口具有相同的名称和不同的包,以区分不可变性:Seq
,immutable.Seq
,mutable.Seq
。
另一方面,这些课程通常不共享名称。 List
是不可变序列,而ListBuffer
是可变序列。有一些例外,例如HashSet
,但这只是与实施有关的巧合。
现在,Array
不是Scala集合的一部分,是一个Java类,但它的包装器WrappedArray
清楚地显示了它将出现的位置:作为一个可变类。
WrappedArray
实现的接口是IndexedSeq
,它存在可变和不可变的特征。
immutable.IndexedSeq
有一些实施类,包括WrappedString
。但是,实现它的一般用法类是Vector
。该类占据Array
类在可变侧占据的相同位置。
现在,使用Vector
而不是使用Array
并不复杂,所以我不知道你为什么称之为复杂。
也许你认为它在内部做得太多,在这种情况下你就错了。所有设计良好的不可变类都是persistent,因为使用不可变集合意味着创建它的新副本,因此必须针对它进行优化,这正是Vector
所做的。
答案 2 :(得分:11)
主要是因为Scala中没有任何数组。你所看到的是java的数组,它们有一些方法可以帮助它们适应集合API。
其他任何东西都不是数组,它具有不遭受类型擦除或破坏方差的独特属性。它只是具有索引和值的另一种类型。 Scala 确实,它被称为IndexedSeq
,如果您需要将其作为数组传递给某些第三方API,那么您只需使用.toArray
答案 3 :(得分:4)
Scala 2.13添加了ArraySeq
,它是数组支持的不可变序列。
答案 4 :(得分:1)
scala Array
类的要点是提供一种机制来访问Java数组的功能(但没有Java的糟糕的设计决策允许数组在其类型系统中是协变的)。 Java数组是可变的,因此scala标准库中的数组也是可变的。
假设库中还有另一个类immutable.Array
,但编译器也使用Java数组作为底层结构(效率/速度)。然后编译并运行以下代码:
val i = immutable.Array("Hello")
i.asInstanceOf[Array[String]](0) = "Goodbye"
println( i(0) ) //I thought i was immutable :-(
也就是说,数组真的是可变的。
答案 5 :(得分:1)
Scala 3 现在有 IArray,一个不可变数组。
它作为 Opaque Type Alias 实现,没有运行时开销。
答案 6 :(得分:0)
Arrays的问题在于它们具有固定的大小。没有操作可以将元素添加到数组中,也不需要从中删除元素。
你可以保留一个你认为足够长的数组作为后备存储,"浪费"您未使用的内存,跟踪上次使用的索引,如果需要额外的空间,请复制到更大的数组。复制显然是O(N)
。
更改单个元素也是O(N)
,因为您需要复制整个数组。没有结构共享,这是高性能功能数据结构的关键。
您还可以为"溢出"分配额外的数组。元素,并以某种方式跟踪您的数组。那时你正在重新发明Vector。
简而言之,由于它们不适用于结构共享,数组的不可变外观对于大多数常见操作(如添加元素,删除元素和更改元素)都具有糟糕的运行时性能特征。
仅留下固定大小的固定内容数据载体的用例,并且该用例相对较少。使用List
,Stream
或Vector
答案 7 :(得分:0)
您可以简单地使用Array [T] .toIndexSeq将Array [T]转换为ArraySeq [T],其类型为immutable.IndexedSeq [T]。 (在Scala 2.13.0之后)
struct B {
int n;
// note: return type for defaulted equality comparison operator
// must be 'bool', not 'auto'
bool operator==(const B& rhs) const noexcept = default;
auto operator<=>(const B& rhs) const noexcept
{
return n <=> rhs.n;
}
};