Немодифицируемый вид изменчивой коллекции 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
делегирование вызовов в базовую коллекцию.