Почему не изменяемые методы из класса Collections не создают коллекции с новыми элементами?
Предположим, есть этот код:
List<String> modifiableList = new ArrayList<String>(Arrays.asList("1","2","3"));
List<String> unmodifiableList = Collections.unmodifiableList(modifiableList);
System.out.println(unmodifiableList);
modifiableList.remove("1");
modifiableList.remove("3");
System.out.println(unmodifiableList);
это печатает
[1, 2, 3]
[2]
Если изменить вторую строку на
List<String> unmodifiableList = Collections.unmodifiableList(
new ArrayList<String>(modifiableList));
это работает как ожидалось. Вопрос в том, почему не UnmodifiableList
внутренний класс из коллекций (и все другие неизменяемые классы) оттуда создают новую копию исходного списка, как и конструктор ArrayList
например, чтобы действительно сделать его неизменным?
Изменить: я понимаю, что делает код; мой вопрос, почему это было реализовано таким образом? Почему конструктор из UnmodifiableList (внутренний класс из Collections) ведет себя как конструктор ArrayList при создании свежей копии базового массива? Почему изменяемая коллекция (ArrayList) копирует весь контент, а неизменяемая коллекция - нет?
3 ответа
Целью этих методов является создание неизменяемого представления существующей коллекции. Это задокументированное поведение, и во многих случаях это именно то, что вы хотите - это гораздо более эффективно, чем, например, копирование всех данных... или вы хотите передать коллекции вызывающих, которые будут отражать любые изменения, которые вы хотите внести, но без разрешения их вносить изменения.
Если вы хотите неизменную копию данных (или, по крайней мере, ссылки...), просто создайте копию, а затем создайте неизменное представление поверх нее - так же, как вы.
Таким образом, в принципе, вы можете легко создать либо представление, либо копирование и просмотр, основываясь на Collections.unmodifiable*
сами выполняющие операцию просмотра. Итак, у нас есть две ортогональные операции:
- Создать копию (например, через конструктор)
- Создать представление (через
Collections.unmodifiable*
)
Эти операции могут быть составлены очень легко. Если Collections.unmodifiable*
фактически выполнял "только копирование", тогда нам потребовались бы другие операции, чтобы просто сделать представление. Если вы согласны с тем, что оба варианта полезны в разных ситуациях, то их компоновка дает большую гибкость.
Причина проста эффективность. Копирование всех элементов коллекции может занять очень много времени, особенно если обернутая коллекция имеет какое-то волшебство, такое как JPA-ленивая загрузка, и требует дополнительной памяти. Упаковка базовой коллекции как есть - тривиальная операция, которая не требует дополнительных затрат. В случае, когда разработчик действительно хочет отдельную копию (неизменяемую или нет), очень легко создать ее явно. (Я склонен использовать гуаву Immutable*
за это.)
Обратите внимание, что unmodifiableList возвращает "неизменяемое представление" предоставленного списка. Таким образом, сам список остается (он все еще может быть изменен), только его "неизменяемое представление" не подлежит изменению. Вы можете думать об этом как о таблицах и представлениях SQL - вы можете запускать сценарии DML для таблиц, и это будет отражено в связанных представлениях. Что касается ArrayList --- он поддерживается... массивом, так что это особенность реализации, заключающаяся в том, что он копирует элементы из предоставленного списка источников (который на самом деле не должен поддерживаться массивом). Это отвечает на ваш вопрос?