Незмінні колекції Java


115

З базової документації колекції Java 1.6 :

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

Другий критерій мене трохи бентежить. З огляду на те, що перша колекція неможливо змінити, і якщо припустити, що оригінальну посилання на колекцію було видалено, про які зміни йдеться у другому рядку? Чи йдеться про зміни в елементах колекції, тобто про стан елементів?

Друге питання:
Щоб колекція була непорушною, як можна запропонувати додаткові вказані гарантії? Якщо стан елемента в колекції оновлюється потоком, чи достатньо для непорушності те, що ці оновлення в стані не видно на потоці, що містить незмінну колекцію?

Щоб колекція була непорушною, як можна взятись за надання додаткових гарантій?


Взагалі (особливо на функціональних мовах) незмінна (відома ще й стійка) колекція може змінитися в тому сенсі, що ви можете отримати новий стан цієї колекції, але в той же час старий стан все ще буде доступний за іншими посиланнями. Наприклад, newCol = oldCol.add("element")буде створено нову колекцію, яка є копією старої з ще 1 елементом, а всі посилання на oldColзаповіт все одно вказуватимуть на ту саму незмінену стару колекцію.
дружина

Відповіді:


154

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

Незмінні колекції взагалі неможливо змінити - вони не обертають іншу колекцію - у них є свої елементи.

Ось цитата з гуави ImmutableList

На відміну від Collections.unmodifiableList(java.util.List<? extends T>), який представляє собою окрему колекцію, яка все ще може змінюватися, екземпляр ImmutableListмістить власні приватні дані і ніколи не зміниться.

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


@Bhaskar - дивіться мій останній абзац.
Божо

1
якщо колекція, яка загорнута всередину іншої немодифікованої колекції, не має ніяких інших посилань на неї, то чи поводження немодифікованої колекції точно збігається з незмінною колекцією?
Бхаскар

1
@Bozho, Якщо посилання на резервну колекцію не надано, і надається лише незмінна посилання на обгортку колекції, то ви не можете її змінити. Тоді чому ви говорите "Ви не можете бути впевнені"? Чи вказує він на сценарій, коли якийсь потік або якимось чином його модифікують, оскільки колекція резервної копії може змінюватися?
AKS

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

... немає жодного способу unmodifiableList, ані будь-якого коду, який його використовує, знати, чи може змінитися загорнута колекція або як.
supercat

86

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

напр

List<String> strings = new ArrayList<String>();
List<String> unmodifiable = Collections.unmodifiableList(strings);
unmodifiable.add("New string"); // will fail at runtime
strings.add("Aha!"); // will succeed
System.out.println(unmodifiable);

1
які різні причини, якщо такі є, можуть внести зміни в колекцію, яку неможливо змінити, за умови, що оригінальна посилання на колекцію не використовується для зміни базової колекції?
Бхаскар

21
Collection<String> c1 = new ArrayList<String>();
c1.add("foo");
Collection<String> c2 = Collections.unmodifiableList(c1);

c1є змінним (тобто не є незмінним і незмінним ).
c2є незмінним : він не може бути змінений сам, але якщо пізніше я змінити c1те , що зміни будуть видні в c2.

Це тому, що c2це просто обгортка навколо, c1а не насправді незалежна копія. Guava надає ImmutableListінтерфейс та деякі реалізації. Вони працюють, фактично створюючи копію вхідних даних (якщо тільки введення не є незмінною колекцією самостійно).

Щодо вашого другого питання:

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


2
@John: ні, це не так. Принаймні, не за визначенням, яке цитує ОП.
Йоахім Зауер

@JohnVint, справді? Я б не думав так, тому що, використовуючи c1, я все-таки можу додати до нього елементи, і це доповнення буде видно c2. Чи це не означає, що це не незмінне?
Бхаскар

Я думаю, якщо c1 може вийти, то це було б непорушним, але незмінність також стосується вмісту колекції. Я більше посилався на немодифікуваний збір java.util.Date's
Джон Вінт

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

1
Дозвольте процитувати визначення: "Колекції, які додатково гарантують ..." (моє наголос). Collection.unmodifiableList() не можу цього гарантувати, оскільки він не може гарантувати, що його аргумент не вийде. Гуава ImmutableList.of завжди створює незмінне List, навіть якщо ви дозволяєте його аргументам уникнути.
Йоахім Зауер

17

Зараз у java 9 є заводські методи для незмінного списку, набору, карти та Map.Entry.

У Java SE 8 та більш ранніх версіях ми можемо використовувати утилітні методи класу Collections, такі як unmodifiableXXX для створення об'єктів Immutable Collection.

Однак ці методи Collections.unmodifiableXXX - це дуже виснажливий та багатослівний підхід. Щоб подолати ці недоліки, корпорація Oracle додала до інтерфейсів List, Set та Map кілька корисних методів.

Тепер у java 9: ​​- Інтерфейси списку та встановлення мають методи “of ()” для створення порожнього чи непорожнього змінного списку чи встановлення об’єктів, як показано нижче:

Приклад порожнього списку

List immutableList = List.of();

Приклад не порожнього списку

List immutableList = List.of("one","two","three");

2
А в Java 10 List.copyOfі Set.copyOfдодано, які дозволяють створити немодифікуючу копію списку / набору або повернути дану колекцію, якщо вона вже неможливо
змінити

6

Я вважаю, що справа в тому, що навіть якщо колекція не може бути змінена, це не гарантує, що вона не може змінитися. Візьмемо для прикладу колекцію, яка виселяє елементи, якщо вони занадто старі. Немодифікується просто означає, що об'єкт, що містить посилання, не може його змінити, не те, що він не може змінити. Справжній приклад цього - Collections.unmodifiableListметод. Він повертає немодифікований вигляд списку. Посилання списку, яке було передано в цей метод, все ще змінюється, тому список може бути змінений будь-яким власником посилання, який був переданий. Це може призвести до ConcurrentModificationExceptions та інших поганих речей.

Незмінний, означає, що колекцію жодним чином не можна змінювати.

Друге питання: Незмінна колекція не означає, що об'єкти, що містяться в колекції, не зміняться, просто ця колекція не зміниться в кількості та складі об'єктів, які вона містить. Іншими словами, список посилань колекції не зміниться. Це не означає, що внутрішні об'єкти, на які посилається, не можуть змінюватися.


Хороший!! Отже, якщо я створю обгортку над немодифікованою колекцією та роблю модифікацію посилання на колекцію приватною, то я можу бути впевнений, що вона незмінна. правильно?
AKS

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

0

Pure4J підтримує те, що ви хочете, двома способами.

По-перше, він надає @ImmutableValueпримітку, щоб ви могли коментувати клас, щоб сказати, що він непорушний. Існує плагін Maven, який дозволяє перевірити, чи дійсно ваш код незмінний (використання finalтощо).

По-друге, він надає стійкі колекції від Clojure (із доданими генеричними засобами) та забезпечує, що елементи, додані до колекцій, незмінні. Виконання цих даних, очевидно, досить добре. Усі колекції є незмінними, але реалізують інтерфейси (та дженерики) колекцій Java для перевірки. Мутація повертає нові колекції.

Відмова: Я розробник цього

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