Немодифицируемый вид изменчивой коллекции Scala

У меня есть класс с частным полем, который является изменчивой коллекцией. Поле в этом конкретном экземпляре является ArrayBuffer, хотя мой вопрос распространяется на любой конечный упорядоченный тип сбора данных с произвольным доступом. Я хочу открыть это поле, не разрешая другим изменять его. В Java я бы добавил метод вроде:

private List<T> theList;

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

В Java мы просто принимаем, что результатом является List, который не полностью реализует интерфейс List, потому что #add и друзья бросают UnsupportedOperationException.

В Scala я ожидал бы найти подходящую черту с такими аксессуарами, как iterator, size и apply (для получения значений по индексу), но без мутаторов. Существует ли такой тип, которого я еще не нашел?

Ответ 1

Библиотека коллекции Scala ориентирована на неизменность, а неизменяемость не относится только к тому факту, что вам не разрешено изменять данную коллекцию, но также и вам гарантировано, что коллекция не будет когда-либо измененный кем-либо.

Таким образом, вы не можете и не должны получать такую ​​коллекцию, как immutable.Seq, как представление из изменяемого буфера в Scala, так как он нарушает эту гарантию.

Но вы можете реализовать концепцию unmodifiable mutable 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

EDIT:

Вероятно, лучшим способом получения немодифицируемого представления коллекции является downcasting до 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 не гарантирует никаких гарантий неизменности, но также не позволяет выполнять какие-либо операции модификации, поэтому кажется, что он идеально подходит.

Ответ 2

Преобразуйте буфер в Seq или одну из других немодифицируемых коллекций, которые являются частью пакета scala.collection.

myBuffer.toSeq

Ответ 3

Если вы хотите:

  • использовать только существующие функции библиотеки (не писать собственную обертку)
  • обязательно избегайте копирования
  • убедитесь, что возвращаемое значение не может быть добавлено к чему-то модифицируемому то одна возможность - просто вернуть итератор

    def getStuff = array.iterator
    

Это не вариант, конечно, если ваша спецификация требует, чтобы вы возвращали Seq, но он

  • позволяет вызывающему пользователю перебирать его с использованием того же синтаксиса, что и для Seq (for (x <- obj.getStuff))
  • позволяет вызывающему абоненту легко преобразовать в Seq или List с помощью obj.getStuff.toSeq или obj.getStuff.toList.

(Обратите внимание, что в документации на .iterator явно не указано, что возвращаемый объект нельзя отнести к чему-то модифицируемому, но текущая реализация ArrayBuffer.iterator действительно дает немодифицируемый итератор.)

Ответ 4

Если myBuffer.toSeq недостаточно хорош, вы можете написать собственный класс, расширяющий scala.collection.IndexedSeqOptimized делегирование вызовов в базовую коллекцию.