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

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

private List<X> _xs;
....
List<X> getXs(){
  return Collections.unmodifiableList(_xs);
}

Но я не могу придумать удобный способ сделать это, если X выше сам по себе является списком:

private List<List<Y>> _yLists;
.....
List<List<Y>> getYLists() {
  return Collections.unmodifiableList(_yLists);
}

Проблема вышеизложенного, конечно же, заключается в том, что хотя клиент не может изменять список списков, он может добавлять/удалять объекты Y из встроенных списков.

Есть предположения?


person Miserable Variable    schedule 06.01.2009    source источник


Ответы (6)


Лучшее, что я смог придумать, использует Список пересылки из коллекций 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));
        }
    });
}
person Miserable Variable    schedule 06.01.2009
comment
Особенно после того, как я сделал внешний список неизменяемым :-) - person Miserable Variable; 06.01.2009
comment
Что произойдет, если это список наборов? - person Craig P. Motlin; 06.01.2009
comment
для списка наборов вы бы написали аналогичный код для набора? То есть это все еще «хак» в том смысле, что эта операция изначально не поддерживается языком, как константа. - person Chii; 07.01.2009
comment
@Motlin: Да, у меня действительно был List‹Map‹K,V››, который я хотел сделать константным, так что да, вам нужен отдельный метод. Хуже того, вы не можете назвать их все немодифицируемыми списками из-за стирания; так что теперь у меня есть немодифицируемыйListList и немодифицируемыйListMap. - person Miserable Variable; 07.01.2009
comment
@Chii: Ну, это такой же хак, как и COllections.unmodifableList, не так ли? Я подозреваю, что когда-нибудь мы увидим аннотации @Unmodifying, @Immutable (для объектов), @NonModifying и @NonMutating (для методов). - person Miserable Variable; 07.01.2009

к сожалению, нет простого способа получить глубокую константность в java. вам придется взломать его, всегда следя за тем, чтобы список внутри списка также не поддавался изменению.

мне тоже было бы интересно узнать какое-нибудь элегантное решение.

person Chii    schedule 06.01.2009

Коллекции clojure (карта, набор, список, вектор) могут быть вложенными и неизменяемыми по умолчанию. Для чистой java есть такая библиотека:

http://code.google.com/p/pcollections/

person Arthur Edelstein    schedule 20.03.2012

Если вы посмотрите на реализацию методов Collections.unmodifying*(...), то увидите, что они просто обертывают коллекцию. Выполнение глубокой утилиты таким же образом должно быть выполнимо.

Недостатком этого является то, что он добавляет дополнительный вызов метода к доступу к коллекции и, таким образом, влияет на производительность.

person iny    schedule 06.01.2009

Если ваша единственная цель здесь — обеспечить инкапсуляцию, классическим решением является использование clone() или аналогичного метода для возврата структуры, которая не является внутренним состоянием объекта. Это, очевидно, работает только в том случае, если все объекты могут быть клонированы, и если скопированная структура достаточно мала.

Если это довольно часто используемая структура данных, другой вариант — сделать API, который обращается к ней, более конкретным, чтобы у вас был более подробный контроль над конкретными вызовами. Написание собственной реализации списка, как указано выше, является одним способом сделать это, но если вы можете сузить вызовы до конкретных вариантов использования, вы можете предоставить определенные API доступа вместо интерфейса списка.

person TREE    schedule 06.01.2009
comment
проблема с созданием собственных интерфейсов вместо использования стандартных java-интерфейсов заключается в том, что вы не можете воспользоваться преимуществами многих служебных библиотек, использующих java-интерфейсы, если только вы их не реализовали. ИМХО, это слишком высокая цена, чтобы платить за глубокую константность. - person Chii; 07.01.2009
comment
Указанный сборник (Список списков) не является стандартным сборником. - person TREE; 09.01.2009

На всякий случай, если кому-то интересно, вот простое решение:

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

person Ludvig W    schedule 12.07.2017