Неизменяемый против Unmodifiable collection

Из Обзор рамок коллекций:

Коллекции, которые не поддерживают операции модификации (например, add, remove и clear), называются не подлежащими модификации. Коллекции, которые не подлежат модификации, могут быть изменены.

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

Я не могу понять разницу.
В чем разница между неизменяемыми и неизменяемыми здесь?

Ответ 1

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

Неизменяемая коллекция гарантирует, что ничто не сможет изменить коллекцию. Если он обертывает модифицируемую коллекцию, он гарантирует, что никакой другой код не имеет доступа к этой модифицируемой коллекции. Обратите внимание: хотя ни один код не может изменять объекты, к которым содержит коллекция ссылки, сами объекты могут быть изменчивыми - создание неизменяемой коллекции StringBuilder не позволяет "заморозить" эти объекты.

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

Ответ 2

В принципе unModifiable Collection - это представление, поэтому косвенно оно все равно может быть "изменено" из какой-либо другой ссылки, которая может быть изменена. Также как его просто просмотр в режиме readonly коллекции annother, когда коллекция исходного кода изменяет unModifiable Collection, всегда будут представлены самые последние значения.

Однако immutable Коллекция может рассматриваться как только для чтения другой коллекции и не может быть изменена. В этом случае, когда изменяется исходная коллекция, неизменяемая коллекция не отражает изменения

Вот тест, чтобы визуализировать эту разницу.

@Test
public void testList() {

    List<String> modifiableList = new ArrayList<String>();
    modifiableList.add("a");

    System.out.println("modifiableList:"+modifiableList);
    System.out.println("--");


    //unModifiableList

    assertEquals(1, modifiableList.size());

    List<String> unModifiableList=Collections.unmodifiableList(
                                        modifiableList);

    modifiableList.add("b");

    boolean exceptionThrown=false;
    try {
        unModifiableList.add("b");
        fail("add supported for unModifiableList!!");
    } catch (UnsupportedOperationException e) {
        exceptionThrown=true;
        System.out.println("unModifiableList.add() not supported");
    }
    assertTrue(exceptionThrown);

    System.out.println("modifiableList:"+modifiableList);
    System.out.println("unModifiableList:"+unModifiableList);

    assertEquals(2, modifiableList.size());
    assertEquals(2, unModifiableList.size());
            System.out.println("--");



            //immutableList


    List<String> immutableList=Collections.unmodifiableList(
                            new ArrayList<String>(modifiableList));

    modifiableList.add("c");

    exceptionThrown=false;
    try {
        immutableList.add("c");
        fail("add supported for immutableList!!");
    } catch (UnsupportedOperationException e) {
        exceptionThrown=true;
        System.out.println("immutableList.add() not supported");
    }
    assertTrue(exceptionThrown);


    System.out.println("modifiableList:"+modifiableList);
    System.out.println("unModifiableList:"+unModifiableList);
    System.out.println("immutableList:"+immutableList);
    System.out.println("--");

    assertEquals(3, modifiableList.size());
    assertEquals(3, unModifiableList.size());
    assertEquals(2, immutableList.size());

}

Выход

modifiableList:[a]
--
unModifiableList.add() not supported
modifiableList:[a, b]
unModifiableList:[a, b]
--
immutableList.add() not supported
modifiableList:[a, b, c]
unModifiableList:[a, b, c]
immutableList:[a, b]
--

Ответ 3

Я думаю, что основное различие заключается в том, что владелец измененной коллекции может захотеть предоставить доступ к коллекции другому коду, но предоставить этот доступ через интерфейс, который не позволяет другому коду изменять коллекцию (в то время как сохраняя эту способность к собственному коду). Таким образом, сбор не является неизменным, но некоторым пользователям не разрешается изменять коллекцию.

Oracle Учебник Java Collection Wrapper может сказать это (выделено мной):

Немодифицируемые обертки имеют два основных вида использования:

  • Сделать сбор неизменным после его создания. В этом случае хорошая практика не поддерживать ссылку на резервную копию коллекция. Это абсолютно гарантирует неизменность.
  • Чтобы позволить определенным клиентам доступ только для чтения к вашим структурам данных. Вы сохраняете ссылку на коллекцию резервных копий, но выведите ссылку на обертку. Таким образом, клиенты могут смотреть, но не измените, пока вы поддерживаете полный доступ.

Ответ 4

Если мы говорим о JDK Unmodifiable* vs guava Immutable*, на самом деле разница также в производительности . Неизменяемые коллекции могут быть как более быстрыми, так и эффективными с точки зрения памяти, если они являются не оболочками вокруг обычных коллекций (реализация JDK - это обертки). Ссылаясь на команду guava:

JDK предоставляет методы Collections.unmodifiableXXX, но, на наш взгляд, это может быть

<... >

  • неэффективны: структуры данных все еще имеют все накладные расходы на изменяемые коллекции, включая проверки параллельной модификации, дополнительное пространство в хэш-таблицах и т.д.

Ответ 5

чтобы процитировать java docs, В отличие от оберток синхронизации, которые добавляют функциональность к завернутой коллекции, немодифицируемые обертки берут функциональность. В частности, они убирают возможность изменять коллекцию, перехватывая все операции, которые будут изменять коллекцию и бросать UnsupportedOperationException.

Немодифицируемые обертки имеют два основных вида использования:

Сделать сбор неизменным после его создания. В этом случае хорошей практикой является не поддерживать ссылку на коллекцию резервных копий. Это абсолютно гарантирует неизменность.

Чтобы разрешить определенным клиентам доступ только для чтения к вашим структурам данных. Вы сохраняете ссылку на коллекцию резервных копий, но передаете ссылку на обертку. Таким образом, клиенты могут просматривать, но не изменять, в то время как вы поддерживаете полный доступ.

Это действительно подводит итог.

Ответ 6

Как отмечалось выше, немодифицируемый не является неизменным, потому что немодифицируемая коллекция может быть изменена, если, например, немодифицируемая коллекция имеет базовый набор делегатов, на который ссылается какой-то другой объект, и этот объект меняет его.

Что касается неизменяемости, он даже не определен. Однако, как правило, это означает, что объект "не изменится", но это нужно будет определить рекурсивно. Например, я могу определить неизменяемый класс, чьи переменные экземпляра являются всеми примитивами и методы которых не содержат аргументов и возвращают примитивы. Затем методы рекурсивно позволяют переменным экземпляра быть неизменными, а все методы содержат неизменяемые аргументы и возвращают неизменяемые значения. Методы должны гарантировать, что они будут возвращать одинаковое значение с течением времени.

Предполагая, что мы можем это сделать, существует также концепция потокобезопасности. И вам может показаться, что неизменяемость (или не измененная с течением времени) также подразумевает безопасность потоков. Однако это не так., и это главное, что я делаю здесь это еще не было отмечено в других ответах. Я могу построить неизменяемый объект, который всегда возвращает те же результаты, но не является потокобезопасным. Чтобы увидеть это, предположим, что я создаю неизменяемую коллекцию, сохраняя дополнения и удаления с течением времени. Теперь неизменная коллекция возвращает свои элементы, просматривая внутреннюю коллекцию (которая может меняться со временем), а затем (внутренне) добавляет и удаляет элементы, которые были добавлены или удалены после создания коллекции. Ясно, что хотя коллекция всегда возвращала те же элементы, она не является потокобезопасной только потому, что она никогда не изменит значение.

Теперь мы можем определить неизменяемые объекты, которые являются потокобезопасными и никогда не изменятся. Существуют рекомендации по созданию неизменяемых классов, которые обычно приводят к таким классам, однако имейте в виду, что могут быть способы создания неизменяемых классов, которые требуют внимания к безопасности потоков, например, как описано в примере коллекции "моментальный снимок" выше.