可变的Scala集合的不可修改的视图

时间:2012-11-21 06:00:07

标签: scala scala-collections

我有一个私有字段的类,它是一个可变集合。此特定实例中的字段为ArrayBuffer,但我的问题扩展到任何有限的,有序的随机访问集合类型。我想暴露这个字段而不允许其他人修改它。在Java中,我会添加一个方法,如:

private List<T> theList;

public List<T> getList() {
    return Collections.unmodifiableList(theList);
}

在Java中,我们只接受结果是List,因为List而朋友抛出#add而没有完全实现UnsupportedOperationException界面。

在Scala中,我希望找到一个适当的特征,包括iteratorsizeapply(用于按索引检索值)但没有变更器的访问器。这样的类型是否存在我还没有找到?

4 个答案:

答案 0 :(得分:9)

Scala集合库面向不变性,不变性并不仅仅指您不允许修改给定集合,而且保证集合不会永远存在由任何人修改

所以你不能并且你不应该将像immutable.Seq这样的集合作为来自Scala中可变缓冲区的视图,因为它打破了这种保证。

但你可以像这样轻松实现不可修改的可变Seq的概念:

class UnmodifiableSeq[A](buffer: mutable.Seq[A]) extends mutable.Seq[A]{
    def update(idx: Int, elem: A) {throw new UnsupportedOperationException()}

    def length = buffer.length

    def apply(idx: Int) = buffer(idx)

    def iterator = buffer.iterator
}

用法:

val xs = Array(1, 2, 3, 4)
val view = new UnmodifiableSeq(xs)
println(view(2)) >> 3
view(2) = 10 >> Exception in thread "main" java.lang.UnsupportedOperationException

编辑:

获取集合的不可修改视图的一种可能更好的方法是向下转换为collection.Seq,它不提供可变更新操作:

val xs = Array(1, 2, 3)
val view: Seq[Int] = xs //this is unmodifiable

或创建一个包含Seq的包装器,如果你有自己的自定义可变类。

class UnmodifiableView[A](col: MutableCollection[A]) extends collection.Seq[A]{
    def length = col.length

    def apply(idx: Int) = col(idx)

    def iterator = col.iterator
}

scala.collection.Seq特征不能保证不变性,但它也不允许任何修改操作,因此它看起来非常合适。

答案 1 :(得分:2)

将您的缓冲区转换为Seq,或scala.collection包中的其他不可修改的集合之一。

myBuffer.toSeq

答案 2 :(得分:1)

如果你想:

  • 仅使用现有的库函数(不编写自己的包装器)
  • 请务必避免复制
  • 确保无法将返回的值强制转换为可修改的值 那么一种可能性就是简单地返回一个迭代器

    def getStuff = array.iterator
    

当然,这不是一个选项,如果您的规范明确要求您返回Seq,但它

  • 允许调用者使用与Seq&#39; s(for (x <- obj.getStuff))相同的语法对其进行迭代
  • 允许调用者使用obj.getStuff.toSeqobj.getStuff.toList轻松转换为Seq或列表。

(注意,.iterator的文档没有明确说明返回的对象不能被强制转换为可修改的东西,但ArrayBuffer.iterator的当前实现确实提供了一个不可修改的迭代器。)

答案 3 :(得分:0)

如果myBuffer.toSeq不够好,您可以编写自己的类,将scala.collection.IndexedSeqOptimized委托给基础集合。