Dlaczego Java 8 nie zawiera niezmiennych kolekcji?

130

Zespół Java wykonał mnóstwo świetnej pracy, usuwając bariery w programowaniu funkcjonalnym w Javie 8. W szczególności zmiany w kolekcjach java.util wykonują świetną robotę, łącząc przekształcenia w bardzo szybko przesyłane strumieniowo operacje. Biorąc pod uwagę, jak dobrą robotę wykonali, dodając pierwszorzędne funkcje i metody funkcjonalne do kolekcji, dlaczego całkowicie nie udało się zapewnić niezmiennych kolekcji, a nawet niezmiennych interfejsów kolekcjonowania?

Bez zmiany żadnego istniejącego kodu zespół Java może w dowolnym momencie dodać niezmienne interfejsy, które są takie same jak zmienne, pomniejszone o metody „set” i sprawić, by istniejące interfejsy rozszerzyły się z nich, w następujący sposób:

                  ImmutableIterable
     ____________/       |
    /                    |
Iterable        ImmutableCollection
   |    _______/    /          \   \___________
   |   /           /            \              \
 Collection  ImmutableList  ImmutableSet  ImmutableMap  ...
    \  \  \_________|______________|__________   |
     \  \___________|____________  |          \  |
      \___________  |            \ |           \ |
                  List            Set           Map ...

Jasne, operacje takie jak List.add () i Map.put () obecnie zwracają wartość logiczną lub poprzednią wartość dla danego klucza, aby wskazać, czy operacja się powiodła, czy nie. Niezmienne kolekcje musiałyby traktować takie metody jak fabryki i zwracać nową kolekcję zawierającą dodany element - co jest niezgodne z obecną sygnaturą. Ale można to obejść, używając innej nazwy metody, takiej jak ImmutableList.append () lub .addAt () i ImmutableMap.putEntry (). Wynikająca z tego gadatliwość byłaby bardziej niż przeważona korzyściami pracy z niezmiennymi kolekcjami, a system typów zapobiegałby błędom wywołania niewłaściwej metody. Z czasem stare metody mogły być przestarzałe.

Zwycięstwa niezmiennych kolekcji:

  • Prostota - rozumowanie na temat kodu jest prostsze, gdy podstawowe dane nie ulegają zmianie.
  • Dokumentacja - jeśli metoda przyjmuje niezmienny interfejs kolekcji, wiesz, że nie będzie modyfikować tej kolekcji. Jeśli metoda zwraca niezmienną kolekcję, wiesz, że nie możesz jej zmodyfikować.
  • Współbieżność - niezmienne kolekcje można bezpiecznie współdzielić między wątkami.

Jako osoba, która próbowała języków, które zakładają niezmienność, bardzo trudno jest wrócić na Dziki Zachód szalonej mutacji. Kolekcje Clojure (abstrakcja sekwencji) mają już wszystko, co zapewniają kolekcje Java 8, oraz niezmienność (choć może zużywa dodatkową pamięć i czas z powodu zsynchronizowanych list połączonych zamiast strumieni). Scala ma zarówno kolekcje zmienne, jak i niezmienne z pełnym zestawem operacji i chociaż operacje te są chętne, wywołanie .iterator daje leniwy widok (i istnieją inne sposoby leniwej ich oceny). Nie rozumiem, jak Java może nadal konkurować bez niezmiennych kolekcji.

Czy ktoś może wskazać mi historię lub dyskusję na ten temat? Z pewnością jest to gdzieś publiczne.

GlenPeterson
źródło
9
W związku z tym - Ayende ostatnio blogowało o kolekcjach i niezmiennych kolekcjach w języku C #, z testami porównawczymi. ayende.com/blog/tags/performance - tl; dr - niezmienność jest powolna .
Oded
20
z twoją hierarchią mogę dać ci ImmutableList, a następnie zmienić ją na ciebie, gdy nie spodziewasz się, że może to zepsuć wiele rzeczy, podobnie jak masz tylko constkolekcje
maniak ratchet
18
@Oded Niezmienność jest powolna, ale blokuje się. Podobnie jest z historią. Prostota / poprawność jest warta prędkości w wielu sytuacjach. Przy małych kolekcjach prędkość nie stanowi problemu. Analiza Ayende opiera się na założeniu, że nie potrzebujesz historii, blokowania lub prostoty i że pracujesz z dużym zestawem danych. Czasami to prawda, ale nie jest to jedna rzecz, która zawsze jest lepsza. Są kompromisy.
GlenPeterson
5
@GlenPeterson, po to są kopie obronne Collections.unmodifiable*(). ale nie traktuj ich jako niezmiennych, gdy nie są
maniakiem zapadkowym,
13
Co? Jeśli twoja funkcja przyjmuje ImmutableListten schemat, ludzie mogą przejść mutable List? Nie, to bardzo złe naruszenie LSP.
Telastyn,

Odpowiedzi:

113

Ponieważ niezmienne kolekcje absolutnie wymagają udostępniania, aby były użyteczne. W przeciwnym razie każda operacja upuszcza gdzieś całą inną listę na stos. Języki całkowicie niezmienne, takie jak Haskell, generują zdumiewające ilości śmieci bez agresywnych optymalizacji i udostępniania. Posiadanie kolekcji, która może być użyta tylko z <50 elementami, nie jest warte umieszczenia w standardowej bibliotece.

Co więcej, niezmienne kolekcje często mają zasadniczo różne implementacje niż ich zmienne odpowiedniki. Rozważmy na przykład ArrayList, że niezmienna niezmienna ArrayListwcale nie byłaby tablicą! Powinien zostać zaimplementowany ze zrównoważonym drzewem o dużym współczynniku rozgałęzienia, Clojure używa 32 IIRC. Sprawienie, by zbiory modyfikowalne były „niezmienne” przez dodanie aktualizacji funkcjonalnej, jest tak samo błędem wydajności, jak przeciek pamięci.

Ponadto udostępnianie nie jest wykonalne w Javie. Java zapewnia zbyt wiele nieograniczonych ograniczeń zmienności i równości referencji, aby udostępnianie było „tylko optymalizacją”. Prawdopodobnie trochę by cię denerwowało, gdybyś mógł zmodyfikować element na liście i zdać sobie sprawę, że właśnie zmodyfikowałeś element w pozostałych 20 wersjach tej listy, którą posiadałeś.

Wyklucza to również ogromne klasy bardzo istotnych optymalizacji dla efektywnej niezmienności, współdzielenia, fuzji strumieniowej, jak to nazywacie, zmienność psuje ją. (To byłby dobry slogan dla ewangelistów FP)

jozefg
źródło
21
Mój przykład mówił o niezmiennych interfejsach . Java mogłaby zapewnić pełny zestaw zarówno modyfikowalnych, jak i niezmiennych implementacji tych interfejsów, które wymagałyby niezbędnych kompromisów. Programiści mogą wybrać odpowiednio zmienne lub niezmienne. Programiści muszą teraz wiedzieć, kiedy używać listy kontra zestawu. Zasadniczo nie potrzebujesz wersji modyfikowalnej, dopóki nie wystąpi problem z wydajnością, a wtedy może być niezbędny tylko jako narzędzie do budowania. W każdym razie posiadanie niezmiennego interfejsu byłoby zwycięstwem samo w sobie.
GlenPeterson
4
Ponownie przeczytałem twoją odpowiedź i myślę, że mówisz, że Java ma fundamentalne założenie dotyczące zmienności (np. Fasola java) i że zbiory są tylko wierzchołkiem góry lodowej, a wycięcie tej końcówki nie rozwiąże podstawowego problemu. Ważny punkt. Mogę przyjąć tę odpowiedź i przyspieszyć przyjęcie Scali! :-)
GlenPeterson,
8
Nie jestem pewien, czy niezmienne kolekcje są przydatne do dzielenia się częściami wspólnymi. Najpopularniejszy niezmienny typ w Javie, niezmienny zbiór znaków, używany do udostępniania, ale już nie. Kluczową rzeczą, która sprawia, że ​​jest przydatna, jest możliwość szybkiego kopiowania danych z Stringdo StringBuffer, manipulowania nimi, a następnie kopiowania danych do nowego niezmiennego String. Użycie takiego wzorca z zestawami i listami może być równie dobre, jak użycie niezmiennych typów, które mają na celu ułatwienie produkcji nieznacznie zmienionych instancji, ale nadal mogą być lepsze ...
supercat
3
Całkowicie możliwe jest utworzenie niezmiennej kolekcji w Javie za pomocą udostępniania. Elementy przechowywane w kolekcji są referencjami, a ich odnośniki mogą być mutowane - co z tego? Takie zachowanie już psuje istniejące kolekcje, takie jak HashMap i TreeSet, ale są one zaimplementowane w Javie. A jeśli wiele kolekcji zawiera odniesienia do tego samego obiektu, całkowicie oczekuje się, że modyfikacja obiektu spowoduje zmianę widoczną, gdy zostanie wyświetlony ze wszystkich kolekcji.
Sekret Solomonoffa,
4
jozefg, jest całkowicie możliwe wdrożenie wydajnych niezmiennych kolekcji na JVM z strukturalnym współdzieleniem. Scala i Clojure mają je jako część swojej standardowej biblioteki, obie implementacje oparte są na HAMT Phila Bagwella (Hash Array Mapped Trie). Twoje stwierdzenie dotyczące Clojure implementującego niezmienne struktury danych z drzewami BALANCED jest całkowicie błędne.
sesm
77

Zmienna kolekcja nie jest podtypem niezmiennej kolekcji. Zamiast tego zbiory zmienne i niezmienne są rodzeństwem potomków kolekcji czytelnych. Niestety, pojęcia „czytelny”, „tylko do odczytu” i „niezmienny” wydają się rozmywać, mimo że oznaczają trzy różne rzeczy.

  • Czytelna podstawowa klasa kolekcji lub typ interfejsu obiecuje, że można czytać elementy, i nie zapewnia żadnych bezpośrednich środków modyfikowania kolekcji, ale nie gwarantuje, że kod odbierający odwołanie nie może rzutować ani manipulować nim w sposób umożliwiający modyfikację.

  • Interfejs kolekcji tylko do odczytu nie zawiera żadnych nowych elementów, ale powinien zostać zaimplementowany tylko przez klasę, która obiecuje, że nie ma sposobu na manipulowanie referencją w taki sposób, aby mutować kolekcję lub otrzymać referencję do czegoś to może to zrobić. Nie obiecuje jednak, że kolekcja nie będzie modyfikowana przez coś innego, co ma odniesienie do elementów wewnętrznych. Należy pamiętać, że interfejs kolekcji tylko do odczytu może nie być w stanie zapobiec implementacji przez klasy zmienne, ale może określić, że dowolną implementację lub klasę pochodną z implementacji, która umożliwia mutację, należy uznać za „nielegalną” implementację lub pochodną implementacji .

  • Niezmienna kolekcja to taka, która zawsze będzie przechowywać te same dane, o ile istnieje jakiekolwiek odniesienie do niej. Każda implementacja niezmiennego interfejsu, który nie zawsze zwraca te same dane w odpowiedzi na konkretne żądanie, jest zepsuta.

Jest to czasami warto mieć silnie powiązane zmienne i niezmienne typy kolekcji, która zarówno wdrożenia lub wynikają z tego samego „czytelnej” typu i mieć typ czytelny zawierać AsImmutable, AsMutableoraz AsNewMutablemetod. Taki projekt pozwala na wywołanie kodu, który chce utrwalić dane w kolekcji AsImmutable; metoda ta utworzy kopię obronną, jeśli kolekcja jest zmienna, ale pomiń kopię, jeśli jest już niezmienna.

supercat
źródło
1
Świetna odpowiedź. Niezmienne kolekcje mogą dać ci dość silną gwarancję związaną z bezpieczeństwem nici i tym, jak możesz o nich myśleć w miarę upływu czasu. Kolekcja do odczytu / tylko do odczytu nie. W rzeczywistości, aby uszanować zasadę podstawienia liskowa, tylko do odczytu i niezmienne powinny prawdopodobnie być abstrakcyjnym typem podstawowym z ostateczną metodą i prywatnymi członkami, aby upewnić się, że żadna klasa pochodna nie może zniszczyć gwarantowanej przez ten typ gwarancji. Lub powinny być w pełni konkretnym typem, który albo otacza kolekcję (tylko do odczytu), albo zawsze ma kopię obronną (niezmienną). W ten sposób robi to guava ImmutableList.
Laurent Bourgault-Roy,
1
@ LaurentBourgault-Roy: Istnieją zarówno typy zapieczętowane, jak i dziedziczne, niezmienne. Jeśli ktoś nie chce dopuścić, aby nieślubna klasa pochodna złamała niezmienniki, typy zapieczętowane mogą zapewnić ochronę przed tym, podczas gdy klasy dziedziczone nie oferują żadnej. Z drugiej strony, może to być możliwe do kodu, który wie coś o danych, które posiada przechowywać go znacznie bardziej zwarty niż typ, który nic nie wie o tym. Rozważmy na przykład typ ReadableIndexedIntSequence, który zawiera sekwencję intmetod, metod getLength()i getItemAt(int).
supercat
1
@ LaurentBourgault-Roy: Biorąc pod uwagę a ReadableIndexedIntSequence, można stworzyć instancję niezmiennego typu wspieranego przez tablicę, kopiując wszystkie elementy do tablicy, ale załóżmy, że konkretna implementacja po prostu zwróciła 16777216 dla długości i ((long)index*index)>>24dla każdego elementu. Byłby to uzasadniony niezmienny ciąg liczb całkowitych, ale skopiowanie go do tablicy byłoby ogromną stratą czasu i pamięci.
supercat
1
W pełni się zgadzam. Moje rozwiązanie zapewnia poprawność (do pewnego stopnia), ale aby uzyskać wydajność z dużym zestawem danych, musisz mieć trwałą strukturę i projekt niezmienności od samego początku. W przypadku niewielkich kolekcji od czasu do czasu można uniknąć niezmiennej kopii. Pamiętam, że Scala przeprowadził analizę różnych programów i stwierdził, że około 90% utworzonych list miało długość 10 lub mniej pozycji.
Laurent Bourgault-Roy,
1
@ LaurentBourgault-Roy: Podstawowe pytanie brzmi, czy można ufać ludziom, że nie będą produkować zepsutych implementacji lub pochodnych klas. Jeśli tak, i jeśli interfejsy / klasy podstawowe zapewniają metody asMutable / asImmutable, może być możliwe poprawienie wydajności o wiele rzędów wielkości [np. Porównanie kosztu wywołania asImmutableinstancji powyżej zdefiniowanej sekwencji z kosztem budowy niezmienna kopia wspierana tablicą]. Sądzę, że zdefiniowanie interfejsów do takich celów jest prawdopodobnie lepsze niż próba zastosowania metod ad-hoc; IMHO, największy powód ...
supercat,
15

Framework kolekcji Java zapewnia możliwość tworzenia kolekcji tylko do odczytu za pomocą sześciu metod statycznych w klasie java.util.Collections :

Jak ktoś zauważył w komentarzach do pierwotnego pytania, zwrócone kolekcje mogą nie zostać uznane za niezmienne, ponieważ chociaż kolekcje nie mogą być modyfikowane (żadnych członków nie można dodawać ani usuwać z takiej kolekcji), rzeczywiste obiekty, do których odwołuje się kolekcja można zmodyfikować, jeśli pozwala na to ich typ obiektu.

Problem ten pozostałby jednak bez względu na to, czy kod zwraca pojedynczy obiekt, czy niezmodyfikowaną kolekcję obiektów. Jeśli typ pozwala zmutować jego obiekty, wówczas decyzja została podjęta w projekcie typu i nie widzę, jak zmiana w JCF mogłaby to zmienić. Jeśli niezmienność jest ważna, członkowie kolekcji powinni być typu niezmiennego.

Arkanon
źródło
4
Projekt niemodyfikowalnych kolekcji zostałby znacznie ulepszony, gdyby opakowania zawierały wskazanie, czy owijana rzecz jest już niezmienna, a byłyby immutableListitd. Metody fabryczne, które zwracałyby opakowanie tylko do odczytu wokół kopii przekazanego lista, chyba że lista przekazana była już niezmienna . Łatwo byłoby stworzyć takie typy zdefiniowane przez użytkownika, ale dla jednego problemu: joesCollections.immutableListmetoda nie mogłaby rozpoznać, że nie trzeba kopiować zwróconego obiektu fredsCollections.immutableList.
supercat
8

To jest bardzo dobre pytanie. Podoba mi się myśl, że z całego kodu napisanego w Javie i działającego na milionach komputerów na całym świecie, każdego dnia, przez całą dobę, należy zmarnować około połowę całkowitych cykli zegara, robiąc tylko bezpieczne kopie kolekcji, które są zwracane przez funkcje. (I zbieranie śmieci te kolekcje milisekund po ich utworzeniu).

Odsetek programistów Java jest świadomy istnienia unmodifiableCollection()rodziny metod Collectionsklasy, ale nawet wśród nich wielu po prostu się tym nie przejmuje.

I nie mogę ich winić: interfejs, który udaje, że jest do odczytu i zapisu, ale rzuca, UnsupportedOperationExceptionjeśli popełnisz błąd przywołania którejkolwiek z jego metod „zapisu”, jest czymś złym!

Teraz interfejs taki, w Collectionktórym brakuje add(), remove()a clear()metody nie byłyby interfejsem „ImmutableCollection”; byłby to interfejs „UnmodifiableCollection”. W rzeczywistości nigdy nie może istnieć interfejs „ImmutableCollection”, ponieważ niezmienność jest naturą implementacji, a nie cechą interfejsu. Wiem, to nie jest bardzo jasne; pozwól mi wyjaśnić.

Załóżmy, że ktoś przekazuje ci taki interfejs kolekcji tylko do odczytu; czy bezpiecznie jest przekazać go do innego wątku? Gdybyście na pewno wiedzieli, że jest to kolekcja niezmienna, odpowiedź brzmiałaby „tak”; niestety, ponieważ jest to interfejs, nie wiesz, w jaki sposób jest on implementowany, więc odpowiedź musi brzmieć „ nie” : dla wszystkich wiesz, może to być niezmodyfikowany (dla ciebie) widok kolekcji, która w rzeczywistości jest modyfikowalna, (jak to, co dostajesz Collections.unmodifiableCollection()), więc próba odczytania z niego, gdy inny wątek jest modyfikowany, spowoduje odczyt uszkodzonych danych.

Tak więc, co zasadniczo opisałeś, to zestaw nie „niezmiennych”, ale „niemodyfikowalnych” interfejsów kolekcji. Ważne jest, aby zrozumieć, że „niemodyfikowalny” oznacza po prostu, że ktokolwiek, kto ma odniesienie do takiego interfejsu, nie może modyfikować podstawowej kolekcji, a im zapobiega się po prostu dlatego, że interfejs nie ma żadnych metod modyfikacji, a nie dlatego, że podstawowa kolekcja jest koniecznie niezmienna. Podstawowa kolekcja może równie dobrze być zmienna; nie macie o tym żadnej wiedzy i kontroli.

Aby mieć niezmienne kolekcje, musiałyby to być klasy , a nie interfejsy!

Te niezmienne klasy kolekcjonerskie musiałyby być ostateczne, więc gdy otrzymasz odniesienie do takiej kolekcji, wiesz na pewno, że będzie się ona zachowywać jak niezmienna kolekcja bez względu na to, co Ty, lub ktokolwiek, kto ma do niej odniesienie, mógłbyś zrób to.

Tak więc, aby mieć pełny zestaw kolekcji w Javie (lub innym deklaratywnym języku imperatywnym), potrzebowalibyśmy:

  1. Zestaw niemodyfikowalnych interfejsów kolekcji .

  2. Zestaw modyfikowalnych zbiórki interfejsów , rozszerzającego żadnych modyfikacji z nich.

  3. Zestaw modyfikowalnych zbiórki klas realizujących zmienny interfejsy, a co za tym idzie również modyfikowalnych interfejsów.

  4. Zbiór niezmiennych klas kolekcji , implementujących niemodyfikowalne interfejsy, ale przeważnie przekazywany jako klasy, aby zagwarantować niezmienność.

Wszystkie powyższe zaimplementowałem dla zabawy i używam ich w projektach, które działają jak urok.

Powodem, dla którego nie są częścią środowiska wykonawczego java, jest prawdopodobnie dlatego, że sądzono, że byłoby to zbyt duże / zbyt złożone / zbyt trudne do zrozumienia.

Osobiście uważam, że to, co opisałem powyżej, nie wystarczy; jeszcze jedna rzecz, która wydaje się potrzebna, to zestaw zmiennych interfejsów i klas dla niezmienności strukturalnej . (Które można po prostu nazwać „Sztywnym”, ponieważ prefiks „StructurallyImmutable” jest zbyt cholernie długi).

Mike Nakis
źródło
Słuszne uwagi. Dwa szczegóły: 1. Niezmienne kolekcje wymagają pewnych podpisów metod, a konkretnie (wykorzystując Listę jako przykład): List<T> add(T t)- wszystkie metody „mutatora” muszą zwrócić nową kolekcję, która odzwierciedla zmianę. 2. Na lepsze lub gorsze interfejsy często stanowią podpis oprócz umowy . Serializable jest jednym z takich interfejsów. Podobnie, program Porównywalny wymaga prawidłowego wdrożenia compareTo()metody, aby działała poprawnie i idealnie była kompatybilna z equals()i hashCode().
GlenPeterson
Och, nawet nie miałem na myśli niezmienności mutacji po kopii. To, co napisałem powyżej, odnosi się do prostych prostych niezmiennych kolekcji, które tak naprawdę nie mają takich metod add(). Ale przypuszczam, że gdyby metody mutatora miały zostać dodane do niezmiennych klas, wówczas musiałyby zwrócić również klasy niezmienne. Jeśli więc czai się tam problem, nie widzę go.
Mike Nakis,
Czy Twoje wdrożenie jest publicznie dostępne? Powinienem był zapytać w tym miesiącu. W każdym razie moje to: github.com/GlenKPeterson/UncleJim
GlenPeterson
4
Suppose someone hands you such a read-only collection interface; is it safe to pass it to another thread?Załóżmy, że ktoś przekazuje ci instancję interfejsu gromadzenia zmiennych. Czy można bezpiecznie wywołać jakąkolwiek metodę? Nie wiesz, że implementacja nie zapętla się wiecznie, nie zgłasza wyjątku lub całkowicie ignoruje umowę interfejsu. Dlaczego podwójny standard specjalnie dla niezmiennych kolekcji?
Doval
1
IMHO twoje rozumowanie przeciwko zmiennym interfejsom jest błędne. Możesz napisać zmienną implementację niezmiennych interfejsów, a następnie się zepsuje. Pewnie. Ale to twoja wina, ponieważ naruszasz umowę. Przestań to robić. Nie różni się niczym od złamania SortedSetpodklasy przez zestaw z niezgodną implementacją. Lub przekazując niekonsekwentnie Comparable. Jeśli chcesz, prawie wszystko można zepsuć. Myślę, że to właśnie @Doval rozumiał przez „podwójne standardy”.
maaartinus
2

Niezmienne kolekcje mogą być głęboko rekurencyjne w porównaniu do siebie nawzajem i nie mogą być nieracjonalnie nieefektywne, jeśli równość obiektów jest realizowana przez secureHash. Nazywa się to lasem merkle. Może być dla każdej kolekcji lub w jej części, jak (samoczynnie równoważące się binarne) drzewo AVL dla posortowanej mapy.

O ile wszystkie obiekty java w tych kolekcjach nie mają unikatowego identyfikatora lub łańcucha bitów do haszowania, kolekcja nie ma nic do haszowania, by móc się jednoznacznie nazwać.

Przykład: Na moim laptopie 4x1.6 GHz mogę uruchomić 200 k sha256s na sekundę najmniejszego rozmiaru, który mieści się w 1 cyklu mieszania (do 55 bajtów), w porównaniu do 500 K operacji HashMap lub 3 mln operacji w tablicy mieszającej długich. Nowe zbiory o wartości 200 K / log (collectionSize) na sekundę są wystarczająco szybkie dla niektórych rzeczy, w których ważna jest integralność danych i anonimowa globalna skalowalność.

Ben Rayfield
źródło
-3

Występ. Kolekcje z natury mogą być bardzo duże. Kopiowanie 1000 elementów do nowej struktury z 1001 elementami zamiast wstawiania jednego elementu jest po prostu okropne.

Konkurencja. Jeśli masz uruchomionych kilka wątków, mogą chcieć pobrać bieżącą wersję kolekcji, a nie wersję przekazaną 12 godzin temu, gdy wątek się rozpoczął.

Przechowywanie. Dzięki niezmiennym obiektom w wielowątkowym środowisku możesz uzyskać dziesiątki kopii tego samego obiektu w różnych punktach jego cyklu życia. Nie ma znaczenia dla obiektu kalendarza lub daty, ale gdy będzie to zbiór 10 000 widżetów, zabije cię.

James Anderson
źródło
12
Niezmienne kolekcje wymagają kopiowania tylko wtedy, gdy nie można ich udostępniać z powodu wszechobecnej zmienności, takiej jak Java. Współbieżność jest ogólnie łatwiejsza w przypadku niezmiennych kolekcji, ponieważ nie wymagają one blokowania; a dla widoczności zawsze możesz mieć zmienne odniesienie do niezmiennej kolekcji (powszechnej w OCaml). Dzięki udostępnianiu aktualizacje mogą być zasadniczo bezpłatne. Możesz dokonywać logarytmicznie większej liczby alokacji niż w przypadku zmiennej struktury, ale podczas aktualizacji wiele wygasłych podobiektów można natychmiast zwolnić lub ponownie użyć, więc niekoniecznie masz większy narzut pamięci.
Jon Purdy,
4
Kilka problemów. Kolekcje w Clojure i Scali są niezmienne, ale obsługują lekkie kopie. Dodanie elementu 1001 oznacza skopiowanie mniej niż 33 elementów oraz dodanie kilku nowych wskaźników. Jeśli dzielisz kolekcję, którą można modyfikować, w wątkach, masz różne problemy z synchronizacją podczas jej zmiany. Operacje takie jak „remove ()” są koszmarne. Również niezmienne kolekcje mogą być budowane w sposób zmienny, a następnie kopiowane raz do niezmiennej wersji bezpiecznej do udostępnienia między wątkami.
GlenPeterson,
4
Używanie współbieżności jako argumentu przeciwko niezmienności jest niezwykłe. Duplikaty również.
Tom Hawtin - tackline
4
Trochę zirytowany głosami tutaj. OP zapytał, dlaczego nie wdrożyli niezmiennych kolekcji, i odpowiedziałem rozważnie na pytanie. Prawdopodobnie jedyną możliwą do zaakceptowania odpowiedzią wśród osób świadomych mody jest „ponieważ popełniły błąd”. Mam trochę doświadczenia w tym, że muszę refaktoryzować duże fragmenty kodu przy użyciu doskonałej klasy BigDecimal wyłącznie z powodu słabej wydajności ze względu na niezmienność 512 razy większą niż użycie podwójnego plus trochę bałaganu w celu poprawienia liczb dziesiętnych.
James Anderson,
3
@JamesAnderson: Moje problemy z twoją odpowiedzią: „Wydajność” - można powiedzieć, że niezmienne kolekcje z prawdziwego życia zawsze wdrażają jakąś formę udostępniania i ponownego wykorzystania, aby uniknąć dokładnie opisanego problemu. „Współbieżność” - argument sprowadza się do „Jeśli chcesz mieć zmienność, to niezmienny obiekt nie działa”. Mam na myśli to, że jeśli istnieje pojęcie „najnowszej wersji tej samej rzeczy”, wówczas coś musi się mutować, albo sama rzecz, albo coś, co jest jej właścicielem. A w „Storage” wydaje się, że mówienie, że zmienność jest czasem niepożądana.
jhominal