为什么scala标准库中没有不可变数组?

时间:2011-01-28 07:00:17

标签: arrays scala

Scala有各种各样的不可变序列,如List,Vector等。我很惊讶地发现没有一个简单数组支持的不可变索引序列的实现(Vector似乎对我的需求太复杂了。)

  • 这有设计理由吗?我在邮件列表上找不到一个好的解释。

  • 您是否建议使用与阵列具有接近相同性能的不可变索引序列?我正在考虑scalaz的ImmutableArray,但它有一些scala trunk的问题。

谢谢

8 个答案:

答案 0 :(得分:31)

您可以将数组转换为序列。

val s: Seq[Int] = Array(1,2,3,4)

数组将隐式转换为WrappedArray。由于类型是Seq,更新操作将不再可用。

答案 1 :(得分:19)

所以,让我们首先区分界面和类。接口是API设计,而类是这种API的实现。

Scala中的接口具有相同的名称和不同的包,以区分不可变性:Seqimmutable.Seqmutable.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。

简而言之,由于它们不适用于结构共享,数组的不可变外观对于大多数常见操作(如添加元素,删除元素和更改元素)都具有糟糕的运行时性能特征。

仅留下固定大小的固定内容数据载体的用例,并且该用例相对较少。使用ListStreamVector

可以更好地投放大部分内容

答案 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;
    }
};