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

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

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.01.2009 09:55:37
6 ОТВЕТОВ

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

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

3
6.01.2009 09:58:26

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

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

0
6.01.2009 10:16:32
РЕШЕНИЕ

Лучшее, что я мог придумать, использует 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));
        }
    });
}
7
6.01.2009 10:38:05
Особенно после того, как я сделал внешний список тоже не
Miserable Variable 6.01.2009 10:39:18
Что произойдет, если это список сетов?
Craig P. Motlin 6.01.2009 15:23:58
для списка наборов, вы бы написать аналогичный код для набора? То есть, это все еще «взлом» в том смысле, что эта операция изначально не поддерживается языком, как const.
Chii 7.01.2009 12:41:55
@Motlin: Да, у меня действительно есть List <Map <K, V >>, который я хотел сделать const, так что да, вам нужен отдельный метод. Хуже того, вы не можете назвать их все unmodifiableList из-за удаления; так что теперь у меня есть unmodifiableListList и unmodifiableListMap.
Miserable Variable 7.01.2009 14:38:02
@Chii: Ну, это такой же хак, как и COllections.unmodifableList, нет? Я подозреваю, что когда-нибудь мы увидим аннотации @Unmodifiable, @Immutable (для объектов), @NonModifying и @NonMutating (для методов).
Miserable Variable 7.01.2009 14:39:49

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

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

0
6.01.2009 14:07:37
проблема с созданием ваших собственных интерфейсов вместо использования стандартных java заключается в том, что вы не можете воспользоваться многими служебными библиотеками, которые используют java-интерфейсы, если только вы их не реализовали. ИМХО, это слишком высокая цена, чтобы платить за глубокую константность.
Chii 7.01.2009 12:43:48
Указанная коллекция (список списков) не является стандартной коллекцией.
TREE 9.01.2009 19:36:16

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

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

2
20.03.2012 19:34:18

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

    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
12.07.2017 13:47:20