Kolekcja niezmienna vs niezmodyfikowalna

170

Z przeglądu struktury kolekcji :

Kolekcje, które nie obsługują operacji modyfikacji (takich jak add, removei clear) są nazywane niemodyfikowalnymi . Kolekcje, których nie można modyfikować, można modyfikować .

Kolekcje, które dodatkowo gwarantują, że żadna zmiana w Collectionobiekcie nie będzie widoczna, nazywane są niezmiennymi . Kolekcje, które nie są niezmienne, są zmienne .

Nie mogę zrozumieć różnicy.
Jaka jest różnica między niemodyfikowalnym a niezmiennym w tym miejscu?

Cratylus
źródło

Odpowiedzi:

207

Niezmodyfikowalna kolekcja jest często otoką otaczającą modyfikowalną kolekcję, do której inny kod może nadal mieć dostęp . Więc chociaż nie możesz wprowadzać żadnych zmian, jeśli masz tylko odniesienie do niemodyfikowalnej kolekcji, nie możesz polegać na tym, że zawartość się nie zmieni.

An niezmienne gwarancje kolekcja że nic nie może zmienić kolekcję więcej. Jeśli otacza modyfikowalną kolekcję, upewnia się, że żaden inny kod nie ma dostępu do tej modyfikowalnej kolekcji. Zauważ, że chociaż żaden kod nie może zmienić obiektów, do których kolekcja zawiera odniesienia, same obiekty mogą nadal być modyfikowalne - tworząc niezmienną kolekcjęStringBuilder nie „zamraża” tych obiektów w jakiś sposób.

Zasadniczo różnica polega na tym, czy inny kod może być w stanie zmienić kolekcję za Twoimi plecami.

Jon Skeet
źródło
63
Niezmienna kolekcja nie gwarantuje, że nic już się nie zmieni. Po prostu upewnia się, że samej kolekcji nie można zmienić (i to nie przez zawijanie, ale przez kopiowanie). Obiekty obecne w kolekcji mogą być nadal modyfikowane i nie ma na nie gwarancji.
Hiery Nomus
8
@HieryNomus: Zauważ, że nie powiedziałem, że nic nie może się zmienić - powiedziałem, że nic nie może zmienić kolekcji.
Jon Skeet,
1
ok, mogłem to źle przeczytać;) Ale dobrze jest to jednak wyjaśnić.
Hiery Nomus
5
Więc co mówisz. Czy dla prawdziwej niezmienności potrzebujesz niezmiennej kolekcji, która zawiera elementy niezmiennego typu.
Evan Plaice,
1
@savanibharat: to zależy od tego, czy istnieje ścieżka kodu, którą można jeszcze modyfikować list. Jeśli coś może później zadzwonić, list.add(10)to collodzwierciedli tę zmianę, więc nie, nie nazwałbym tego niezmiennym.
Jon Skeet
92

Zasadniczo unModifiablekolekcja jest widokiem, więc pośrednio może być nadal „modyfikowana” z innego odniesienia, które można modyfikować. Również jako widok tylko do odczytu innej kolekcji, gdy kolekcja źródłowa ulegnie zmianie, niemodyfikowalna kolekcja będzie zawsze prezentować najnowsze wartości.

Jednak immutableCollection można traktować jako kopię tylko do odczytu innej kolekcji i nie można jej modyfikować. W takim przypadku, gdy zmienia się kolekcja źródłowa, niezmienna kolekcja nie odzwierciedla zmian

Oto przykład do wizualizacji tej różnicy.

@Test
public void testList() {

    List<String> modifiableList = new ArrayList<String>();
    modifiableList.add("a");

    System.out.println("modifiableList:"+modifiableList);
    System.out.println("--");


    //unModifiableList

    assertEquals(1, modifiableList.size());

    List<String> unModifiableList=Collections.unmodifiableList(
                                        modifiableList);

    modifiableList.add("b");

    boolean exceptionThrown=false;
    try {
        unModifiableList.add("b");
        fail("add supported for unModifiableList!!");
    } catch (UnsupportedOperationException e) {
        exceptionThrown=true;
        System.out.println("unModifiableList.add() not supported");
    }
    assertTrue(exceptionThrown);

    System.out.println("modifiableList:"+modifiableList);
    System.out.println("unModifiableList:"+unModifiableList);

    assertEquals(2, modifiableList.size());
    assertEquals(2, unModifiableList.size());
            System.out.println("--");



            //immutableList


    List<String> immutableList=Collections.unmodifiableList(
                            new ArrayList<String>(modifiableList));

    modifiableList.add("c");

    exceptionThrown=false;
    try {
        immutableList.add("c");
        fail("add supported for immutableList!!");
    } catch (UnsupportedOperationException e) {
        exceptionThrown=true;
        System.out.println("immutableList.add() not supported");
    }
    assertTrue(exceptionThrown);


    System.out.println("modifiableList:"+modifiableList);
    System.out.println("unModifiableList:"+unModifiableList);
    System.out.println("immutableList:"+immutableList);
    System.out.println("--");

    assertEquals(3, modifiableList.size());
    assertEquals(3, unModifiableList.size());
    assertEquals(2, immutableList.size());

}

Wynik

modifiableList:[a]
--
unModifiableList.add() not supported
modifiableList:[a, b]
unModifiableList:[a, b]
--
immutableList.add() not supported
modifiableList:[a, b, c]
unModifiableList:[a, b, c]
immutableList:[a, b]
--
Prashant Bhate
źródło
Nie widzę żadnej różnicy. Czy możesz wskazać, czym różni się Immutable? Widzę, że zarówno niezmienne, jak i niemodyfikowalne generują błąd, a dodawanie nie jest obsługiwane. Czy coś mi umyka?
AKS
2
@AKS Proszę zobaczyć wynik ostatnich trzech wpisów listy po dodaniu „c” do listy, podczas gdy zarówno rozmiar, jak modifiableListi rozmiar unModifiableListzwiększony immutableListnie uległy zmianie
Prashant Bhate
1
O! Rozumiem! :) .. Więc tutaj zmodyfikowałeś unmodifableList używając zmian w modifiableList, ale ImmutableList nie może być modyfikowany. Ale w ten sam sposób możesz zmodyfikować ImmutableList również, myślę, że tutaj klient uzyska dostęp tylko do odwołania do ImmutableList, odwołanie do modifiableList, za pomocą którego utworzono ImmutableList, nie zostanie ujawnione klientowi. dobrze?
AKS
1
tak, ponieważ nie ma odniesienia do new ArrayList<String>(modifiableList)immutableList nie można zmodyfikować
Prashant Bhate
@PrashantBhate Cześć, nie ma odniesienia do new ArrayList<String>(modifiableList)powodu new? Dzięki.
Unheilig
11

Myślę, że główna różnica polega na tym, że właściciel mutowalnej kolekcji może chcieć zapewnić dostęp do kolekcji do innego kodu, ale zapewnić ten dostęp przez interfejs, który nie pozwala innemu kodowi na modyfikowanie kolekcji (przy jednoczesnym zastrzeganiu tej możliwości do kodu właściciela). Dlatego kolekcja nie jest niezmienna, ale niektórzy użytkownicy nie mogą zmieniać kolekcji.

Samouczek Oracle Java Collection Wrapper ma do powiedzenia (dodano uwydatnienie):

Niemodyfikowalne opakowania mają dwa główne zastosowania:

  • Aby kolekcja stała się niezmienna po jej utworzeniu. W takim przypadku dobrą praktyką jest nie utrzymywanie odniesienia do kolekcji zapasowej. To absolutnie gwarantuje niezmienność.
  • Aby umożliwić niektórym klientom dostęp tylko do odczytu do struktur danych. Zachowujesz odniesienie do kolekcji podkładowej, ale udostępniasz odniesienie do opakowania. W ten sposób klienci mogą wyglądać, ale nie modyfikować, zachowując pełny dostęp .
Michael Burr
źródło
3

Jeśli mówimy o JDK Unmodifiable*vs guawa Immutable*, w rzeczywistości różnica dotyczy również wydajności . Zbiory niezmienne może być zarówno szybsze i bardziej wydajne pamięci, jeśli są one nie obwolut wokół stałych kolekcjach (JDK implementacje są obwolut). Cytując zespół guawy :

JDK udostępnia metody Collections.unmodifiableXXX, ale naszym zdaniem mogą to być

<...>

  • nieefektywne: struktury danych nadal mają cały narzut związany z modyfikowalnymi kolekcjami, w tym współbieżne kontrole modyfikacji, dodatkowe miejsce w tablicach skrótów itp.
Dmide
źródło
myśląc o wydajności, należy również wziąć pod uwagę, że niemodyfikowalny wrapper nie kopiuje kolekcji, podczas gdy niezmienna wersja używana w guawie, a teraz także w jdk9 + z np. List.of(...)faktycznie kopiuje dwa razy!
benez
2

Cytując samouczki Java ™ :

W przeciwieństwie do otok synchronizacyjnych, które dodają funkcjonalność do opakowanej kolekcji, niemodyfikowalne opakowania zabierają funkcjonalność. W szczególności zabierają możliwość modyfikowania kolekcji przez przechwytywanie wszystkich operacji, które modyfikowałyby kolekcję i zgłaszanie wyjątku UnsupportedOperationException . Niemodyfikowalne opakowania mają dwa główne zastosowania:

  • Aby kolekcja stała się niezmienna po jej utworzeniu. W takim przypadku dobrą praktyką jest nie utrzymywanie odniesienia do kolekcji zapasowej. To absolutnie gwarantuje niezmienność.

  • Aby umożliwić niektórym klientom dostęp tylko do odczytu do struktur danych. Zachowujesz odniesienie do kolekcji podkładowej, ale udostępniasz odniesienie do opakowania. W ten sposób klienci mogą wyglądać, ale nie modyfikować, zachowując pełny dostęp.

(podkreślenie moje)

To naprawdę podsumowuje.

Bharat
źródło
1

Jak wspomniano powyżej niemodyfikowalna nie jest niezmienna, ponieważ niemodyfikowalna kolekcja może zostać zmieniona, jeśli na przykład niemodyfikowalna kolekcja ma bazową kolekcję delegatów, do której odwołuje się jakiś inny obiekt, a ten obiekt ją zmienia.

Jeśli chodzi o niezmienne, nie jest nawet dobrze zdefiniowane. Jednak ogólnie oznacza to, że obiekt „nie ulegnie zmianie”, ale należałoby to zdefiniować rekurencyjnie. Na przykład mogę zdefiniować niezmienne w klasach, których zmienne instancji są wszystkimi elementami pierwotnymi i których metody nie zawierają żadnych argumentów i zwracają prymitywy. Następnie metody rekurencyjnie zezwalają, aby zmienne wystąpienia były niezmienne, a wszystkie metody zawierały niezmienne argumenty, które zwracają niezmienne wartości. Należy zagwarantować, że metody będą zwracać tę samą wartość w czasie.

Zakładając, że możemy to zrobić, istnieje również pojęcie bezpieczne. I możesz uwierzyć, że niezmienny (lub niezmienny w czasie) oznacza również bezpieczeństwo wątków. Jednak tak nie jesti to jest główna kwestia, którą tutaj poruszam, która nie została jeszcze odnotowana w innych odpowiedziach. Mogę skonstruować niezmienny obiekt, który zawsze zwraca te same wyniki, ale nie jest bezpieczny wątkowo. Aby to zobaczyć, załóżmy, że konstruuję niezmienną kolekcję, utrzymując dodawanie i usuwanie w czasie. Teraz niezmienna kolekcja zwraca swoje elementy, patrząc na kolekcję wewnętrzną (która może się zmieniać w czasie), a następnie (wewnętrznie) dodając i usuwając elementy, które zostały dodane lub usunięte po utworzeniu kolekcji. Oczywiście, chociaż kolekcja zawsze zwracałaby te same elementy, nie jest bezpieczna wątkowo tylko dlatego, że nigdy nie zmieni wartości.

Teraz możemy zdefiniować niezmienne jako obiekty, które są bezpieczne dla wątków i nigdy się nie zmienią. Istnieją wytyczne dotyczące tworzenia niezmiennych klas, które generalnie prowadzą do takich klas, jednak należy pamiętać, że mogą istnieć sposoby tworzenia niezmiennych klas, które wymagają uwagi na bezpieczeństwo wątków, na przykład, jak opisano w powyższym przykładzie kolekcji „migawka”.

dan b
źródło
1

Samouczki Java ™ zawierają następujące informacje:

W przeciwieństwie do otok synchronizacyjnych, które dodają funkcjonalność do opakowanej kolekcji, niemodyfikowalne opakowania zabierają funkcjonalność. W szczególności zabierają możliwość modyfikowania kolekcji przez przechwytywanie wszystkich operacji, które modyfikowałyby kolekcję i zgłaszanie wyjątku UnsupportedOperationException. Niemodyfikowalne opakowania mają dwa główne zastosowania:

Aby kolekcja stała się niezmienna po jej utworzeniu. W takim przypadku dobrą praktyką jest nie utrzymywanie odniesienia do kolekcji zapasowej. To absolutnie gwarantuje niezmienność.

Aby umożliwić niektórym klientom dostęp tylko do odczytu do struktur danych. Zachowujesz odniesienie do kolekcji podkładowej, ale udostępniasz odniesienie do opakowania. W ten sposób klienci mogą wyglądać, ale nie modyfikować, zachowując pełny dostęp.

Myślę, że to wystarczająco dobre wyjaśnienie, aby zrozumieć różnicę.

piyush121
źródło