Как создать глубокую неизменяемую коллекцию?
Я часто делаю поле сбора неизменяемым, прежде чем вернуть его из метода получения:
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 из встроенных списков.
Какие-нибудь мысли?
6 ответов
Лучшее, что я мог придумать, использует ForwardingList из Google Collections. Комментарии приветствуются.
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));
}
});
}
К сожалению, в Java нет простого способа получить глубокое постоянство. вам придется взломать его, убедившись, что список внутри списка также нельзя изменить.
мне тоже было бы интересно узнать какое-нибудь элегантное решение.
Коллекции clojure (map, set, list, vector) могут быть вложенными и неизменными по умолчанию. Для чистой Java, есть эта библиотека:
На случай, если кто-то заинтересован, вот простое решение:
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);
}
Это может быть использовано, например, как решение, если вы хотите предоставить список с помощью метода getList(), вы можете вернуть: toUnmodifiable(mNestedList), где mNestedList - это закрытый список в классе.
Я лично нашел это полезным при реализации класса, используемого для анализа с GSON в Android, так как нет смысла иметь возможность изменять ответ, в этом случае десериализованный json, я использовал этот метод как способ показать список с получателем и убедитесь, что список не будет изменен.
Если вы посмотрите на реализацию методов Collections.unmodifiable*(...), вы увидите, что они просто обертывают коллекцию. Делать глубокую утилиту таким же образом должно быть выполнимо.
Недостатком этого является то, что он добавляет дополнительный вызов метода к доступу к коллекции и таким образом влияет на производительность.
Если ваша единственная цель здесь - обеспечить инкапсуляцию, классическое решение - использовать clone() или подобное для возврата структуры, которая не является внутренним состоянием объекта. Очевидно, это работает только в том случае, если все объекты могут быть клонированы, и если скопированная структура достаточно мала.
Если это довольно часто используемая структура данных, другой вариант - сделать API, который обращается к ней, более конкретным, чтобы у вас был более подробный контроль над конкретными вызовами. Написание собственной реализации List, как описано выше, является одним из способов сделать это, но если вы можете сузить вызовы до конкретных случаев использования, вы можете предоставить доступ к определенным API доступа вместо интерфейса List.