Unmodifiable view of a mutable Scala collection

爷,独闯天下 提交于 2019-12-05 01:41:49

The Scala collection library is oriented to immutability, and immutability doesn't refer only to the fact that you are not allowed to modify a given collection, but also that you are guaranteed that the collection won't be ever modified by anyone.

So you cannot and you shouldn't get a collection like immutable.Seq as a view from a mutable buffer in Scala since it breaks that guarantee.

But you can implement the concept of unmodifiable mutable Seq easy enough like so:

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
}

Usage:

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

EDIT :

A probably better way of obtaining an unmodifiable view of the collection is by downcasting to collection.Seq which provides no mutable update operations:

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

or creating a wrapper which extends Seq if you have your own custom mutable class.

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

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

    def iterator = col.iterator
}

The scala.collection.Seq trait does not make any guarantees of immutability but it also does not allow any modifying operations so it seems the perfect fit.

Convert your buffer to a Seq, or one of the other unmodifiable collections that are part of the scala.collection package.

myBuffer.toSeq

If you want to:

  • use only existing library functions (not write your own wrapper)
  • make sure to avoid copying
  • make sure that the returned value cannot be cast to something modifiable then one possibility is to simply return an iterator

    def getStuff = array.iterator
    

This is not an option, of course, if your specification specifically requires you to return a Seq, but it

  • allows the caller to iterate over it using the same syntax as for Seq's (for (x <- obj.getStuff))
  • allows the caller to easily convert to a Seq or List by using obj.getStuff.toSeq or obj.getStuff.toList.

(Note that the documentation of .iterator does not explicitly say that the returned object cannot be cast to something modifiable, but the current implementation of ArrayBuffer.iterator indeed gives an unmodifiable iterator.)

If myBuffer.toSeq isn't good enough, you can write your own class extending scala.collection.IndexedSeqOptimized delegating the calls to an underlying collection.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!