如何编写一个在Scala中具有自定义索引范围的有效类数组结构?

时间:2018-05-26 00:47:15

标签: arrays scala scala-collections

我想要类似Pascal的变量索引数组,其中用户指定有效的索引范围。而不是将其内容索引的默认选择为0到length-1,我希望第一个索引是10,000,而最后一个索引是20,000。

Scala非常灵活,我想要的是可行的。但是,需要遵守规则来创建与其他集合协调的数据结构。

一个“明显”的解决方案可能是简单地将大小指定为20,000并浪费前半部分内存,但这并不好。此外,可以使地图结构快速工作。但是HashMap所涉及的开销是一个数组级结构的数量级,如我所描述的。

理想情况下,它不应该重复或重新发明已有的代码,也不应该过于复杂,甚至内部。

作为我想要的一个例子,考虑一个具有从500到999的有效索引的数组。我将我的类称为IndexedArray,以区别于Scala中使用的标准数组。此外,我将使用Scala的传统范围来指定声明中的索引范围。

为了简单起见,我不需要我的数据结构有一个不同的步幅。所有索引都是连续的。

var histogram = new IndexedArray[Int](500 to 999)
histogram(500) = 10  // "first" entry has value 10
histogram(999) += 1  // "last" entry has value 1

当然,只需使用标准的Scala数组,并通过每次访问重新映射索引,就可以解决问题。但这不是错误的秘诀吗?我希望数据结构隐藏我眼中的细节。

我的数据结构当然是可变的,因为Scala中的标准数组是可变的;并且行为的唯一区别是索引将在每次访问时自动重新映射。

1 个答案:

答案 0 :(得分:0)

事实证明,至少得到一个现在像宣传的那样有效的基本结构并不困难。我真的更希望我的类扩展Scala集合中已有的适当结构,以便与其他可变集合API无形地合并。但是到目前为止我还没能做到这一点。

然而,我的基本解决方案通过使用与标准Scala数组相同的语法来显示Scala的强大功能

class IndexedArray[T: ClassTag](val range: Range)  {

  val size: Int = range.end - range.start + 1
  val start: Int = range.start
  val array = new Array[T](size)

  def apply(index: Int): T = array(index - start).asInstanceOf[T]

  lazy val elemTag: ClassTag[T] = ClassTag[T](array.getClass.getComponentType)

  def length: Int = array.length

  def update(index: Int, elem: T): Unit = array.update( index - start, elem )

}

现在可以使用上述语法实例化数据结构,并使用如下的数据结构。 (对于Scala新手,以下两行是等效的,并且做同样的事情。)

var pascalLike = IndexedArray[Int](500 until 1000) 

var pascalLike = IndexedArray[Int](500 to 999) 

然后,我们可以使用该结构。

pascalLike(500) = 100          // Sets the first element to 100
pascalLike(500) += 1           // Adds 1 to the first element
pascalLike(501) -= 2           // Subtracts 2 from the second element
println(pascalLike(501))       // Prints "-2"

我不知道如何制作这种结构" flow"与Scala集合API的其余部分一起使用。例如,我希望zipWithIndex索引方法能够像人们对这样的数据结构所期望的那样工作,使用适合于结构的索引。

现在,合并这个人的唯一方法就是抓住底层的array成员并希望Scala编译器允许隐式转换与用户想要做的事情相协调,这并不是很好符合我原来的目标。