2012-11-21 10 views
8

Mam klasę z prywatnym polem, które jest zmienną kolekcją. Pole w tym konkretnym przypadku to ArrayBuffer, chociaż moje pytanie dotyczy dowolnego skończonego, uporządkowanego typu kolekcji o swobodnym dostępie. Chcę udostępnić to pole bez pozwolenia innym na jego modyfikację. W Javie Chciałbym dodać metodę jak:Niezmodyfikowany widok zmiennego zbioru Scala

private List<T> theList; 

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

w Javie po prostu przyjąć, że wynik jest List że nie w pełni implementować interfejs List ponieważ #add i przyjaciele rzucają UnsupportedOperationException.

W Scali, oczekiwałbym znalezienia odpowiedniej cechy z akcesorami takimi jak iterator, size i apply (do pobierania wartości według indeksu), ale bez mutatorów. Czy istnieje taki typ, którego jeszcze nie znalazłem?

+0

Czy coś metody '.toVector', co chcesz? Zwróci kopię wartości w 'ArrayBuffer' jako' Vector', który jest niezmienny i obsługuje szybki losowy dostęp. ('.toVector' jest tylko w Scali 2.10, dla 2.9.2 możesz zrobić coś takiego jak' Vector() ++ myArrayBuffer') – adelbertc

+0

Rozmiar kolekcji może być w dziesiątkach tysięcy. Czy to wystarczająco duże, aby uzasadnić moją niechęć do kopiowania niezmiennych kolekcji? –

+0

Twój przykład Java nie tworzy niezmiennej listy. Po prostu ukrywa zmienne listy w opakowaniu immutabl-owskim. Każdy obiekt zachowujący odniesienie do listy bazowej może ją zmutować ... Jeśli ci to wystarczy, możesz łatwo zastosować to samo podejście ... – paradigmatic

Odpowiedz

9

Biblioteka kolekcji Scala jest skierowany do niezmienności, a niezmienność nie odnosi się tylko do faktu, że nie są dopuszczone do modyfikowania danej kolekcji, ale także, że masz gwarantowane, że kolekcja nie będzie być kiedykolwiek modyfikowanym przez kogoś innego.

Więc nie możesz i nie powinieneś dostać kolekcji takiej jak niezmienna.Seq jako widok z bufora zmiennego w Scali, ponieważ łamie tę gwarancję.

Ale można wdrożyć koncepcję niemodyfikowalne zmienny Seq dość łatwo tak:

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 
} 

Zastosowanie:

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:

chyba lepiej sposobem uzyskania niemodyfikowalnym widokiem kolekcji jest downcasting do collection.Seq, który nie daje możliwości zmiany operacji aktualizacji:

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

lub tworzenia owijkę, która rozciąga Seq jeśli masz swój własny zmienny klasę.

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 cecha nie czyni żadnych gwarancji niezmienności ale również nie pozwala na jakiekolwiek operacje modyfikujące więc wydaje się idealne dopasowanie.

+1

Zgadzam się, błędem byłoby złamać gwarancję niezmienności niezmiennego.Seq, ale czy nie jest to również zły pomysł, aby zerwać kontrakt mutable.Seq # update (Int, A)? –

+1

@ ChristusMartin - Tak, zgadzam się. Muszę wyznać, że popełniłem błąd w mojej odpowiedzi, ponieważ założyłem, że collection.Seq jest aliasem dla collection.immutable.Seq. Jak się okazuje, tak nie jest. Więc możesz rozszerzyć collectionSeq, który nie oferuje gwarancji niezmienności. Dodam poprawkę w mojej odpowiedzi. –

+0

To idealne, dzięki. Nie spodziewałem się też, że będą istniały trzy cechy Seq (kolekcja, collection.immutable, collection.mutable). Wciąż trochę rozczarowujące, że Scala nie ma jeszcze klasy opakowania. Przypuszczam, że po prostu rzucę do kolekcji. Po prostu przestanę być paranoikiem na temat możliwości niewłaściwego użycia. –

2

Konwertuj bufor na Seq lub jedną z innych niemodyfikowalnych kolekcji, które są częścią pakietu scala.collection.

myBuffer.toSeq 
+0

Dobra (być może wystarczająco dobra) sugestia - ale nie zapewnia naprawdę niemodyfikowalności zwróconej wartości. Naprawdę głupi współpracownik mógłby po prostu rzucić Seq z powrotem do ArrayBuffer i wprowadzić w nim zmiany. –

+2

Nie, nie mógł –

+2

Po prostu wypróbowałem i działa ... scala> import collection.mutable.ArrayBuffer; val a = new ArrayBuffer [Int]; a.append (1); val b: Seq [Int] = a.toSeq; b.asInstanceOf [ArrayBuffer [Int]] .endend (2); –

0

Jeśli myBuffer.toSeq nie jest wystarczająco dobry, możesz napisać własną klasę rozszerzającą scala.collection.IndexedSeqOptimized delegującą połączenia do kolekcji podstawowej.

1

Jeśli chcesz:

  • użycie tylko istniejących funkcji bibliotecznych (nie napisać własny otoki)
  • upewnij się, aby uniknąć kopiowania
  • upewnić się, że zwrócona wartość nie może być oddane do czegoś modyfikować czym jedna z możliwości polega po prostu na powrót iterację

    def getStuff = array.iterator 
    

To nie wchodzi w grę, oczywiście, jeżeli specyfikacja wyraźnie wymaga, aby powrócić seq, ale

  • pozwala rozmówcy iteracyjne nad nim przy użyciu tej samej składni dla SEQ'S (for (x <- obj.getStuff))
  • pozwala dzwoniącemu łatwo przekonwertować na Seq lub List za pomocą obj.getStuff.toSeq lub obj.getStuff.toList.

(Należy pamiętać, że dokumentacja .iterator nie wyraźnie powiedzieć, że zwracany obiekt nie może być oddane do czegoś modyfikowalny, ale obecna implementacja ArrayBuffer.iterator rzeczywiście daje niemodyfikowalne iterator).

Powiązane problemy