Как создать глубокую неизменяемую коллекцию? - PullRequest
14 голосов
/ 06 января 2009

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

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 ]

7 голосов
/ 06 января 2009

Лучшее, что я могу придумать, использует 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));
        }
    });
}
3 голосов
/ 06 января 2009

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

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

2 голосов
/ 20 марта 2012

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

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

0 голосов
/ 12 июля 2017

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

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

0 голосов
/ 06 января 2009

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

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

0 голосов
/ 06 января 2009

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

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

...