Как создать глубокую немодифицируемую коллекцию?

Я часто делаю поле коллекции немодифицированным, прежде чем возвращать его из метода геттера:

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 я использовал этот метод как способ показать список с помощью геттера и убедиться, что этот список не будет изменен.