Collections.unmodifiableList: примеры (JAVA)
Collections.unmodifiableList(List extends T> list): ListОбщее описание
Метод Collections.unmodifiableList из пакета java.util возвращает «незменяемый вид» (unmodifiable view) на переданный список. При попытке изменить возвращённую коллекцию будет выброшено исключение UnsupportedOperationException. Метод не копирует элементы, поэтому любые изменения в оригинальном списке будут видны через возвращённый объект.
Сигнатура:
public static <T> List<T> unmodifiableList(List<? extends T> list)
Параметры и поведение:
list- исходный список. Если переданnull, будет выброшеноNullPointerException.- Метод возвращает объект типа
List<T>, представляющий неизменяемое представление исходного списка. - Модифицирующие операции на возвращённом списке (например,
add,remove,clear) приводят кUnsupportedOperationException. - Если исходный список изменяется напрямую (через ссылку на исходный объект), изменения отражаются в «незменяемом виде».
- Если исходный список реализует
RandomAccess, внутренняя реализация может использовать оптимизированный класс, но для пользователя это прозрачно. - Сериализуемость: возвращаемый список будет сериализуем, если исходный список сериализуем.
Типичные сценарии использования: возврат коллекции из API без возможности её изменения вызвавшим кодом, защита внутреннего состояния объекта при сохранении возможности обновлять его через внутренние ссылки.
Простые примеры использования
1) Базовая обёртка и попытка модификации
import java.util.*;
public class Example1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
List<String> unmod = Collections.unmodifiableList(list);
System.out.println(unmod);
unmod.add("C"); // приведёт к исключению
}
}
[A, B]
Exception in thread "main" java.lang.UnsupportedOperationException
at java.base/java.util.Collections$UnmodifiableCollection.add(Collections.java:1055)
...
2) Воздействие на исходный список
import java.util.*;
public class Example2 {
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("X", "Y"));
List<String> view = Collections.unmodifiableList(list);
System.out.println(view);
list.add("Z");
System.out.println(view);
}
}
[X, Y] [X, Y, Z]
3) Null-параметр
List<String> v = Collections.unmodifiableList(null);
Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:221)
at java.base/java.util.Collections.unmodifiableList(Collections.java:??? )
...Похожие возможности в Java
- List.of(...)
- List.copyOf(Collection)
- Collections.synchronizedList
- Guava ImmutableList
Возвращает неизменяемую (immutable) реализацию списка. Создаёт копию и запрещает любые модификации. Предпочтительнее, когда нужна действительно неизменяемая коллекция, безопасная при дальнейшей передаче.
С Java 10 создаёт неисменяемую копию коллекции. Если переданная коллекция уже неизменяема, может вернуть её напрямую. Полезно для создания снимка (snapshot) без зависимости от оригинала.
Не делает коллекцию неизменяемой, но обеспечивает синхронизацию. Часто комбинируется: Collections.unmodifiableList(Collections.synchronizedList(list)) для возвращения потокобезопасного и незменяемого вида.
Библиотечный вариант, создаёт настоящую неизменяемую коллекцию с дополнительными методами. Предпочтителен при работе с Guava и при необходимости расширенных возможностей.
Аналоги в других языках и отличия
- Python
Для неизменяемого списка используется tuple. Кортеж обеспечивает глубокую неизменяемость по структуре, но вложенные изменяемые объекты всё ещё можно менять.
lst = [1, 2]
tpl = tuple(lst)
print(tpl)
(1, 2)
Переменная, объявленная через const, не делает сам массив неизменяемым. Для поверхностной заморозки используется Object.freeze, для глубокой - сторонние библиотеки (Immutable.js).
const arr = [1,2];
const frozen = Object.freeze(arr);
console.log(frozen);
frozen.push(3); // TypeError in strict mode or silent failure
[1, 2] TypeError: Cannot add property 2, object is not extensible
ReadOnlyCollection<T> или метод List<T>.AsReadOnly() возвращают представление без методов изменения. Есть также System.Collections.Immutable.ImmutableList для настоящей неизменяемости.
var list = new List<int>{1,2};
var ro = list.AsReadOnly();
Console.WriteLine(string.Join(",", ro));
1,2 // ro.Add(3) недоступен, метод Add у ReadOnlyCollection отсутствует
listOf(...) создаёт неизменяемый список. Интерфейс List в Kotlin по умолчанию только для чтения; при необходимости копии используется toList().
val mutable = mutableListOf(1,2)
val ro = mutable.toList()
println(ro)
[1, 2]
Нет встроенной неизменяемости для срезов. Часто создаётся копия для защиты от изменений.
s := []int{1,2}
copy := append([]int(nil), s...)
fmt.Println(copy)
[1 2]
Массивы изменяемы, для неизменяемости применяются соглашения или объекты-обёртки. SPL и сторонние библиотеки предоставляют решения.
Можно использовать метатаблицы для создания readonly-таблицы. Это требует явной реализации.
Типичные ошибки и ловушки
- Ожидание полной неизменяемости
Collections.unmodifiableList создаёт только вид. Если сохраняется ссылка на исходный список, он может быть изменён извне, и изменения отразятся в "незменяемом" виде.
List<String> original = new ArrayList<>(List.of("a"));
List<String> view = Collections.unmodifiableList(original);
original.set(0, "b");
System.out.println(view.get(0));
b
Передача null приводит к NullPointerException.
Если создать вид от list.subList(...), поведение зависит от реализации подсписка: операции над исходным списком могут приводить к ConcurrentModificationException или к отражению изменений в видимой коллекции.
unmodifiableList не обеспечивает синхронизацию. Для многопоточного доступа требуется внешняя синхронизация или synchronizedList/immutable коллекции.
Изменения и сопутствующие улучшения
- Стабильность API
- Новые альтернативы в JDK
Метод присутствует с ранних версий JDK (java.util.Collections) и не претерпел существенных изменений.
С Java 9 добавлен List.of, с Java 10 - List.copyOf. Эти методы предлагают более удобные и в ряде случаев безопасные способы получения неизменяемых списков (snapshots либо неизменяемые структуры), поэтому при разработке современных приложений рекомендуется рассматривать их наряду с Collections.unmodifiableList.
Продвинутые и редкие сценарии применения
1) Возврат защищённого вида из API, но с возможностью изменения внутри класса
public class Repo {
private final List<String> internal = new ArrayList<>();
public Repo() {
internal.add("one");
}
public List<String> getItems() {
return Collections.unmodifiableList(internal);
}
public void addItem(String s) {
internal.add(s); // изменение видно вызывающему через view
}
}
// Использование
Repo r = new Repo();
List<String> view = r.getItems();
System.out.println(view);
r.addItem("two");
System.out.println(view);
[one] [one, two]
Комментарий: полезно при необходимости защищать от прямого мутирования клиентом, но при этом обновлять содержимое через методы класса.
2) Комбинация с synchronizedList для потокобезопасного незменяемого представления
List<String> list = Collections.synchronizedList(new ArrayList<>());
list.add("x");
List<String> safeView = Collections.unmodifiableList(list);
// При итерировании требуется синхронизация по list
synchronized(list) {
for (String s : safeView) {
System.out.println(s);
}
}
x
Комментарий: unmodifiableList не обеспечивает синхронизацию; сочетание с synchronizedList создаёт потокобезопасный доступ для чтения через вид.
3) Работа с subList и последствия ConcurrentModification
List<Integer> base = new ArrayList<>(List.of(1,2,3,4));
List<Integer> sub = base.subList(1,3); // элементы 2,3
List<Integer> view = Collections.unmodifiableList(sub);
System.out.println(view);
base.add(99); // модификация базового списка меняет структуру
try {
System.out.println(view.get(0)); // возможен ConcurrentModificationException при итерации
} catch (Exception e) {
e.printStackTrace();
}
[2, 3]
java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1043)
...
Комментарий: subList связывается с оригиналом; изменение оригинала может нарушить итерацию по виду.
4) Сериализация возвращённого вида при сериализуемом исходнике
// Если оригинал Serializable, то и unmodifiableList будет Serializable
ArrayList<String> orig = new ArrayList<>(List.of("a","b"));
List<String> view = Collections.unmodifiableList(orig);
// view можно сериализовать стандартными средствами
(нет вывода, но сериализация возможна если orig сериализуем)
5) Сравнение с List.copyOf для получения snapshot
List<String> src = new ArrayList<>(List.of("a"));
List<String> view = Collections.unmodifiableList(src);
List<String> copy = List.copyOf(src);
src.add("b");
System.out.println(view); // отразит b
System.out.println(copy); // останется без b
[a, b] [a]
Комментарий: List.copyOf подходит для создания неизменяемого снимка; unmodifiableList полезен при желании получить вид без копирования.
джава Collections.unmodifiableList function comments
- джава Collections.unmodifiableList - аргументы и возвращаемое значение
- Функция java Collections.unmodifiableList - описание
- Collections.unmodifiableList - примеры
- Collections.unmodifiableList - похожие методы на java
- Collections.unmodifiableList на javascript, c#, python, php
- Collections.unmodifiableList изменения java
- Примеры Collections.unmodifiableList на джава