【问题标题】:Why no immutable arrays in scala standard library?为什么scala标准库中没有不可变数组?
【发布时间】:2011-01-28 07:00:16
【问题描述】:

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

  • 这是否有设计原因?我在邮件列表中找不到很好的解释。

  • 您是否建议使用与数组性能接近的不可变索引序列?我正在考虑使用 scalaz 的 ImmutableArray,但它在 scala trunk 方面存在一些问题。

谢谢

【问题讨论】:

  • Vector 太复杂了怎么办?你不应该修补它的内脏。
  • “我正在考虑使用 scalaz 的 ImmutableArray,但它在 scala trunk 方面存在一些问题。”那么这需要修复:)
  • scalaz 有一个单独的 2.9.x 分支

标签: arrays scala


【解决方案1】:

Scala 2.13 添加了ArraySeq,这是一个由数组支持的不可变序列。

【讨论】:

    【解决方案2】:

    Scala 3 现在有 IArray,一个不可变数组。

    它被实现为Opaque Type Alias,没有运行时开销。

    【讨论】:

      【解决方案3】:

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

      Scala 中的接口具有相同的名称和不同的包以区分不变性:Seqimmutable.Seqmutable.Seq

      另一方面,这些类通常不共享名称。 List 是不可变序列,而 ListBuffer 是可变序列。也有例外,例如 HashSet,但这只是实施方面的巧合。

      现在,Array 不是 Scala 集合的一部分,它是一个 Java 类,但它的包装器 WrappedArray 清楚地显示了它会出现在哪里:作为一个可变类。

      WrappedArray实现的接口IndexedSeq,既存在可变trait又存在不可变trait。

      immutable.IndexedSeq 有几个实现类,包括WrappedString。然而,实现它的通用类是Vector。该类占据与 Array 类在可变侧占据的相同位置。

      现在,使用Vector 并不比使用Array 更复杂,所以我不知道你为什么称它为复杂。

      也许你认为它在内部做的太多了,在这种情况下你就错了。所有精心设计的不可变类都是persistent,因为使用不可变集合意味着创建它的新副本,因此必须为此优化它们,这正是Vector 所做的。

      【讨论】:

      • 这是一个清晰的解释,但向量访问至少比原始数组访问慢 4-6 倍(如果您使用基元数组,则为 10-30 倍,因为 Vector 尚未专门化并且您有拳击惩罚也是如此)。 OP要求性能。 Vector 无法提供“接近相同的性能”。
      • @Rex 众所周知,除了专业化之外,这是不可能的。即使有了专业化,我认为我们也会偶然发现仍然需要支付罚款的情况。 Java 有一个非类型擦除的Array 不是 Scala 的错,但不允许其他人这样做。
      • 即使除了专业化之外,Vector 的访问速度也很慢。我的 4-6 倍惩罚是调用存储在 WrappedArrayVector 中的类的方法。
      • 使用 Vector 而不是 Array 会带来包袱,正如 Haoyi 先生所证明的那样:lihaoyi.com/post/BenchmarkingScalaCollections.html 当然只有在一定大小之后才重要
      【解决方案4】:

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

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

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

      【讨论】:

      • 不幸的是,s.asInstanceOf[mutable.WrappedArray[Int]].update(1, 42) 有效。
      • @KonstantinPelepelin 所以?
      • 所以它不是不可变的,这是请求的。
      • @KonstantinPelepelin 你确实意识到通过强制转换你正在劫持类型系统,并且从那时起所有关于类型安全的讨论都毫无意义?
      • @Nikita Volkov 类型系统不允许向下转换。在 Scala 惯用代码中,x match { case mutable.Seq ... } 会以同样的悲伤效果击中。
      【解决方案5】:

      主要是因为 Scala 中没有任何数组。您所看到的是 java 的数组使用了一些方法来帮助它们适应集合 API。

      其他任何东西都不会是数组,因为它的独特属性是不会遭受类型擦除或破坏的方差。它只是另一种具有索引和值的类型。 Scala确实有这个,它被称为IndexedSeq,如果你需要将它作为一个数组传递给一些第3方API,那么你可以使用.toArray

      【讨论】:

      • “没有任何数组”具有高度误导性。集合层次结构中几乎所有内容都有一个“.toArray”方法,该方法被转换为普通的旧 Java 数组,Scala 在处理此类数组时会向 Java 发出相同的字节码。
      • @rex - 确切地说,这些数组是 JVM 中的一种特殊构造,它们不是 scala 提供的任何东西,而是它提供的 spring 上下文工厂。虽然它当然可以使用它们。
      【解决方案6】:

      数组的问题在于它们的大小是固定的。没有向数组添加元素或从中删除元素的操作。

      您可以保留一个您认为足够长的数组作为后备存储,“浪费”您未使用的内存,跟踪上次使用的索引,并在需要额外的时复制到更大的数组空间。那个抄袭显然是O(N)

      更改单个元素也是O(N),因为您需要复制整个数组。没有结构共享,这是高性能功能数据结构的关键。

      您还可以为“溢出”元素分配一个额外的数组,并以某种方式跟踪您的数组。那时,您正在重新发明 Vector。

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

      只剩下固定大小的固定内容数据载体的用例,而且这种用例相对较少。大多数用途与ListStreamVector 一起使用效果更好

      【讨论】:

      • 您的 cmets 对 mutable 数组有效,但不适用于大小和内容不会改变的 immutable 数组。
      • @pndc 我看不出我的任何 cmets 对可变数组更有效——性能特征是相同的(而且通常很糟糕,添加元素的 O(n))或更好与不可变数组相比——更改元素是可变数组的 O(1),而不是不可变数组外观的 O(n)。
      【解决方案7】:

      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 :-(
      

      也就是说,数组确实是可变的。

      【讨论】:

      • 如果我们有immutable.Array,我们可以将我们现在拥有的东西移动到mutable.Array,并且在一个公共超类型中只有非更新方法(类似于其他集合)。那你的“反例”就行不通了。
      • 不可变数组只需要一个数组支持。您不需要使支持集合可访问。
      【解决方案8】:

      您可以简单地使用 Array[T].toIndexSeq 将 Array[T] 转换为 ArraySeq[T],其类型为 immutable.IndexedSeq[T]。 (在 Scala 2.13.0 之后)

      scala> val array = Array(0, 1, 2)
      array: Array[Int] = Array(0, 1, 2)
      
      scala> array.toIndexedSeq
      res0: IndexedSeq[Int] = ArraySeq(0, 1, 2)
      

      【讨论】:

        猜你喜欢
        • 2016-03-19
        • 1970-01-01
        • 2011-05-30
        • 2014-06-28
        • 1970-01-01
        • 2017-01-13
        • 2021-11-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多