Как создать глубокую немодифицируемую коллекцию?
Я часто делаю поле коллекции немодифицированным, прежде чем возвращать его из метода геттера:
private List<X> _xs;
....
List<X> getXs(){
return Collections.unmodifiableList(_xs);
}
Но я не могу придумать удобный способ сделать это, если X выше сам является List:
private List<List<Y>> _yLists;
.....
List<List<Y>> getYLists() {
return Collections.unmodifiableList(_yLists);
}
Проблема в том, что, несмотря на то, что клиент не может изменять список списков, он может добавлять/удалять объекты Y из встроенных списков.
Любые мысли?
Ответы
Ответ 1
Лучшее, что я мог придумать, использует ForwardingList из Коллекций Google. Комментарии приветствуются.
private static <T> List<List<T>> unmodifiableList2(final List<List<T>> input) {
return Collections.unmodifiableList(new ForwardingList<List<T>>() {
@Override protected List<List<T>> delegate() {
return Collections.unmodifiableList(input);
}
@Override public List<T> get(int index) {
return Collections.unmodifiableList(delegate().get(index));
}
});
}
Ответ 2
К сожалению, нет простого способа получить глубокую константу в java. вам придется взломать его, всегда убедившись, что список внутри списка также не поддается изменению.
Мне тоже было бы интересно узнать какое-нибудь элегантное решение.
Ответ 3
Коллекции clojure (map, set, list, vector) могут быть вложенными и неизменяемыми по умолчанию. Для чистой java существует эта библиотека:
http://code.google.com/p/pcollections/
Ответ 4
Если вы посмотрите на реализацию методов Collections.unmodifiable * (...), вы увидите, что они просто обертывают коллекцию. Выполнение глубокой утилиты таким же образом должно выполняться.
Недостатком этого является то, что он добавляет дополнительный вызов метода в доступ к коллекции и, таким образом, влияет на производительность.
Ответ 5
Если ваша единственная цель - обеспечить инкапсуляцию, классическое решение - использовать clone() или подобное, чтобы вернуть структуру, которая не является внутренним состоянием объекта. Это, очевидно, работает только в том случае, если все объекты могут быть клонированы, и если скопированная структура достаточно мала.
Если это довольно часто используемая структура данных, другой вариант заключается в том, чтобы сделать API, который обращается к нему более конкретным, чтобы у вас был более подробный контроль над конкретными вызовами. Написание собственной реализации списка, как описано выше, является одним из способов сделать это, но если вы можете сузить вызовы в конкретных случаях использования, вы можете открыть конкретные API доступа вместо интерфейса List.
Ответ 6
На всякий случай кого-то интересует здесь простое решение:
public List<List<Double>> toUnmodifiable(List<List<Double>> nestedList) {
List<List<Double>> listWithUnmodifiableLists = new ArrayList<>();
for (List<Double> list : nestedList) {
listWithUnmodifiableLists
.add(Collections.unmodifiableList(list));
}
return Collections.unmodifiableList(listWithUnmodifiableLists);
}
Это можно использовать, например, в качестве решения, если u хочет выставить список с помощью метода getList(), вы можете вернуть: toUnmodifiable (mNestedList), где mNestedList - частный список в классе.
Я лично нашел это полезным при внедрении класса, используемого для синтаксического разбора с помощью GSON в Android, так как нет смысла модифицировать ответ, в этом случае де-сериализованный json я использовал этот метод как способ показать список с помощью геттера и убедиться, что этот список не будет изменен.