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

StackOverflow https://stackoverflow.com/questions/415967

  •  03-07-2019
  •  | 
  •  

Вопрос

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

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 из встроенных списков.

Есть мысли?

Это было полезно?

Решение

Лучшее, что я мог придумать, - это использовать Список пересылки из коллекций 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));
        }
    });
}

Другие советы

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top