Незмінна колекція проти немодифікованої


170

З огляду Рамок колекцій :

Колекції, які не підтримують операції з модифікації (наприклад add, removeта clear), називаються незмінними . Колекції, які не можуть бути модифіковані, можуть змінюватися .

Колекції, які додатково гарантують, що жодні зміни Collectionоб’єкта не будуть помітні, називаються незмінними . Колекції, які не є непорушними, змінюються .

Я не можу зрозуміти різницю.
Яка тут різниця між незмінним та незмінним ?

Відповіді:


207

Колекція, що не змінюється, часто є обгорткою навколо модифікованої колекції, до якої інший код все ще може мати доступ . Тож якщо ви не можете внести будь-які зміни до нього, якщо у вас є лише посилання на немодифіковану колекцію, ви не можете покладатися на те, що вміст не змінюється.

An незмінні гарантує , що колекція нічого не може змінити колекцію більше. Якщо він обертає колекцію, що змінюється, вона гарантує, що жоден інший код не має доступу до цієї колекції, що змінюється. Зауважте, що хоча жоден код не може змінити, на які об’єкти колекції містяться посилання, самі об’єкти все ще можуть бути зміненими - створення незмінної колекції StringBuilderне якось "заморожує" ці об'єкти.

По суті, різниця полягає в тому, чи може інший код змінити колекцію за вашою спиною.


63
Незмінна колекція не гарантує, що нічого більше не може змінитися. Це просто переконує, що саму колекцію не можна змінити (і не шляхом упаковки, а копіювання). Об'єкти, що знаходяться в колекції, все ще можуть бути змінені, і на них не надається гарантія.
Hiery Nomus

8
@HieryNomus: Зауважте, що я не сказав, що нічого не може змінитися - я сказав, що нічого не може змінити колекцію.
Джон Скіт

1
ок, можливо, це неправильно прочитали;) Але добре це все-таки уточнити.
Hiery Nomus

5
Отже, що ви говорите. Для справжньої непорушності вам потрібна незмінна колекція, що містить предмети незмінного типу.
Еван Плейс

1
@savanibharat: це залежить від того, чи існує який-небудь шлях коду, який ще можна змінити list. Якщо що - щось не пізніше може зателефонувати , list.add(10)то collбуде відображати ці зміни, так що немає, я б не назвав це незмінним.
Джон Скіт

92

В основному unModifiableколекція - це вигляд, тому опосередковано її все одно можна «модифікувати» з якоїсь іншої посилання, яка може змінюватися. Крім того, що це лише лише перегляд лише іншої колекції, коли колекція джерела змінюється, неможливо змінити колекцію завжди з останніми значеннями.

Однак immutableколекція може розглядатися як копія іншої колекції, що читається тільки вдруге, і не може бути змінена. У цьому випадку, коли колекція джерела змінюється, незмінна колекція не відображає зміни

Ось тестовий зразок для візуалізації цієї різниці.

@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());

}

Вихідні дані

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]
--

Я не в змозі побачити будь-яку різницю. Чи можете ви, будь ласка, зазначити, чим відрізняється Immutable? Я можу бачити, як Іммунізовані, так і Немодифікуючі помилки кидають, а додавання не підтримується Я щось тут пропускаю?
AKS

2
@AKS Будь ласка, дивіться висновок останніх трьох записів у списку після додавання "c" до списку, тоді як розмір modifiableListі unModifiableListзбільшений immutableListрозмір не змінилися
Prashant Bhate

1
Ой! зрозумів! :) .. Отже, тут ви змінили unmodifableList, використовуючи зміни в modifiableList, але ImmutableList неможливо змінити. Але так само ви можете змінити ImmutableList, я думаю, тут клієнт отримає доступ лише до посилання ImmutableList, посилання на modifiableList, за допомогою якого створений ImmutableList, не буде піддаватися клієнту. правильно?
AKS

1
так, оскільки немає посилання на new ArrayList<String>(modifiableList)незмінний список не може бути змінено
Prashant Bhate

@PrashantBhate Привіт, немає посилань на це new ArrayList<String>(modifiableList)через new? Дякую.
Unheilig

11

Я думаю, що головна відмінність полягає в тому, що власник змінної колекції може захотіти надати доступ до колекції до якогось іншого коду, але забезпечити цей доступ через інтерфейс, який не дозволяє іншому коду змінювати колекцію (зберігаючи цю можливість до коду володіння). Таким чином, колекція не змінюється, але певні користувачі не можуть змінювати колекцію.

У підручнику Oracle Java Wrapper для колекції є сказане (наголос додано):

Немодифіковані обгортки мають два основних напрямки:

  • Щоб зробити колекцію непорушною після її побудови. У цьому випадку є хорошою практикою не зберігати посилання на колекцію резервної копії. Це абсолютно гарантує незмінність.
  • Дозволити певним клієнтам доступ лише до читання ваших структур даних. Ви зберігаєте посилання на колекцію підкладки, але роздаєте посилання на обгортку. Таким чином, клієнти можуть виглядати, але не змінювати, поки ви підтримуєте повний доступ .

3

Якщо ми говоримо про JDK Unmodifiable*проти гуави Immutable*, насправді різниця полягає також у продуктивності . Колекції, що змінюються, можуть бути як швидшими, так і ефективнішими пам'яті, якщо вони не обгортки навколо звичайних колекцій (реалізації JDK - обгортки). Посилаючись на команду guava :

JDK пропонує методи Collections.unmodifiableXXX, але, на наш погляд, це може бути

<...>

  • неефективно: структури даних все ще мають накладні набори змінних колекцій, включаючи паралельні перевірки модифікацій, додатковий простір у хеш-таблицях тощо.

Думаючи про ефективність, ви також повинні врахувати, що незмінна обгортка не копіює колекцію, де як незмінна версія, що використовується в гуаві, а тепер також у jdk9 +, наприклад List.of(...), справді копіюється двічі!
benez

2

Щоб цитувати підручники Java ™ :

На відміну від обгортки синхронізації, що додає функціональності оберненій колекції, немодифіковані обгортки забирають функціональність. Зокрема, вони забирають можливість змінювати колекцію, перехоплюючи всі операції, які модифікують колекцію, і кидаючи UnsupportedOperationException . Немодифіковані обгортки мають два основних напрямки:

  • Щоб зробити колекцію непорушною після її побудови. У цьому випадку є хорошою практикою не зберігати посилання на колекцію резервної копії. Це абсолютно гарантує незмінність.

  • Дозволити певним клієнтам доступ лише до читання ваших структур даних. Ви зберігаєте посилання на колекцію підкладки, але роздаєте посилання на обгортку. Таким чином, клієнти можуть виглядати, але не змінювати, поки ви підтримуєте повний доступ.

(наголос мій)

Це справді резюмує це.


1

Як зазначалося вище, немодифікується не є незмінним, оскільки немодифікується колекція може бути змінена, якщо, наприклад, немодифікована колекція має базовий делегатський набір, на який посилається якийсь інший об'єкт, і цей об'єкт змінює його.

Щодо непорушного, він навіть не добре визначений. Однак загалом це означає, що об'єкт "не зміниться", але це потрібно визначити рекурсивно. Наприклад, я можу визначити незмінні для класів, чиї змінні екземпляри є усі примітиви, чиї методи не містять аргументів і повертають примітиви. Потім методи рекурсивно дозволяють змінним екземплярам бути незмінними, а всі методи містити аргументи, незмінні та повертають незмінні значення. Методи повинні гарантувати повернення однакового значення з часом.

Якщо припустити, що ми можемо це зробити, існує також безпечна концепція. І вас можуть привести до думки, що незмінна (або не змінна з часом) також передбачає безпеку нитки. Однак це не такі це головний момент, який я тут зазначаю, що ще не було відзначено в інших відповідях. Я можу створити незмінний об’єкт, який завжди повертає однакові результати, але не є безпечним для потоків. Щоб побачити це, припустимо, що я будую незмінну колекцію, підтримуючи доповнення та видалення з часом. Тепер незмінна колекція повертає свої елементи, дивлячись на внутрішню колекцію (яка може змінюватися з часом), а потім (внутрішньо) додавати та видаляти елементи, які були додані чи видалені після створення колекції. Зрозуміло, що хоча колекція завжди повертає однакові елементи, вона не є безпечною для нитки лише тому, що вона ніколи не змінить значення.

Тепер ми можемо визначити незмінних об'єктів як безпечні для потоків і ніколи не змінюватимуться. Існують вказівки щодо створення непорушних класів, які, як правило, призводять до таких класів, проте майте на увазі, що можуть бути способи створення непорушних класів, які потребують уваги до безпеки потоку, наприклад, як описано у прикладі збірки "знімок" вище.


1

Навчальні програми Java ™ говорять про наступне:

На відміну від обгортки синхронізації, що додає функціональності оберненій колекції, немодифіковані обгортки забирають функціональність. Зокрема, вони забирають можливість змінювати колекцію, перехоплюючи всі операції, які модифікують колекцію, і кидаючи UnsupportedOperationException. Немодифіковані обгортки мають два основних напрямки:

Щоб зробити колекцію непорушною після її побудови. У цьому випадку є хорошою практикою не зберігати посилання на колекцію резервної копії. Це абсолютно гарантує незмінність.

Дозволити певним клієнтам доступ лише до читання ваших структур даних. Ви зберігаєте посилання на колекцію підкладки, але роздаєте посилання на обгортку. Таким чином, клієнти можуть виглядати, але не змінювати, поки ви підтримуєте повний доступ.

Я думаю, що це досить хороше пояснення, щоб зрозуміти різницю.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.