Coleção imutável vs não modificável

170

Na visão geral da estrutura de coleções :

Coleções que não suportam operações de modificação (como add, removee clear) são referidas como não modificáveis . Coleções que não são modificáveis são modificáveis .

As coleções que garantem adicionalmente que nenhuma alteração no Collectionobjeto será visível são chamadas de imutáveis . Coleções que não são imutáveis ​​são mutáveis .

Não consigo entender a distinção.
Qual é a diferença entre unmodifiable e immutable aqui?

Cratylus
fonte

Respostas:

207

Uma coleção não modificável geralmente é um invólucro em torno de uma coleção modificável à qual outro código ainda pode ter acesso . Portanto, embora não seja possível fazer alterações se você tiver apenas uma referência à coleção não modificável, não poderá confiar no conteúdo que não está sendo alterado.

Uma coleção imutável garante que nada mais pode mudar a coleção. Se agrupar uma coleção modificável, garante que nenhum outro código tenha acesso a essa coleção modificável. Observe que, embora nenhum código possa alterar a quais objetos a coleção contém referências, os próprios objetos ainda podem ser mutáveis ​​- criar uma coleção imutável de StringBuilderalguma forma "congela" esses objetos.

Basicamente, a diferença é se outro código poderá alterar a coleção pelas suas costas.

Jon Skeet
fonte
63
Uma coleção imutável não garante que nada possa mudar mais. Apenas garante que a coleção em si não possa ser alterada (e não por quebra, mas por cópia). Os objetos presentes na coleção ainda podem ser alterados e nenhuma garantia é dada sobre eles.
Hiery Nomus
8
@HieryNomus: Observe que eu não disse que nada poderia mudar - eu disse que nada poderia mudar a coleção.
perfil completo de Jon Skeet
1
ok, pode ter interpretado mal isso;) Mas é bom esclarecer isso.
Hiery Nomus
5
Então, o que você está dizendo. Para a verdadeira imutabilidade, você precisa de uma coleção imutável que contenha itens de um tipo imutável.
Evan Plaice
1
@savanibharat: isso depende se existe algum caminho de código que ainda possa ser modificado list. Se algo puder chamar mais tarde list.add(10), collrefletirá essa alteração, então não, eu não chamaria de imutável.
precisa
92

Basicamente, unModifiableColeção é uma visão. Portanto, indiretamente, ainda pode ser 'modificada' a partir de alguma outra referência que seja modificável. Também como apenas uma visualização somente leitura de outra coleção, quando a coleção de origem muda, a Coleção não modificável sempre apresentará os valores mais recentes.

No entanto, a immutablecoleção pode ser tratada como uma cópia somente leitura de outra coleção e não pode ser modificada. Nesse caso, quando a coleção de origem é alterada, a coleção imutável não reflete as alterações

Aqui está uma caixa de teste para visualizar essa diferença.

@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());

}

Resultado

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]
--
Prashant Bhate
fonte
Não consigo ver nenhuma diferença. Você pode apontar o quão imutável é diferente? Eu posso ver que Imutável e Não Modificável estão lançando erro e a adição não é suportada. Estou faltando alguma coisa aqui?
AKS
2
@AKS Por favor, ver a saída dos últimos três entradas da lista após a adição de 'c' para a lista, enquanto tanto o tamanho de modifiableListe unModifiableListaumentou immutableListo tamanho não mudou
Prashant Bhate
1
Oh! Entendi! :) .. Então, aqui você modificou unmodifableList usando as alterações em modifiableList, mas ImmutableList não pode ser modificado. Mas da mesma maneira que você pode modificar o ImmutableList também, acho que aqui o cliente terá acesso apenas à referência ImmutableList, a referência ao modifiableList, usando a qual ImmutableList é criada, não será exposta ao cliente. certo?
AKS
1
sim, porque não há nenhuma referência a new ArrayList<String>(modifiableList)ImmutableList não pode ser modificado
Prashant Bhate
@PrashantBhate Olá, não há referência a new ArrayList<String>(modifiableList)causa de new? Obrigado.
Unheilig
11

Eu acho que a principal diferença é que o proprietário de uma coleção mutável pode querer fornecer acesso à coleção para algum outro código, mas fornecer esse acesso por meio de uma interface que não permita que outro código modifique a coleção (enquanto reserva esse recurso para o código proprietário). Portanto, a coleção não é imutável, mas alguns usuários não têm permissão para alterar a coleção.

O tutorial do Java Collection Wrapper da Oracle tem a dizer (ênfase adicionada):

Os invólucros não modificáveis ​​têm dois usos principais, a seguir:

  • Tornar uma coleção imutável depois de construída. Nesse caso, é uma boa prática não manter uma referência à coleção de backup. Isso absolutamente garante imutabilidade.
  • Permitir que determinados clientes tenham acesso somente leitura às suas estruturas de dados. Você mantém uma referência à coleção de backup, mas distribui uma referência ao wrapper. Dessa maneira, os clientes podem olhar, mas não podem modificar, enquanto você mantém o acesso total .
Michael Burr
fonte
3

Se estamos falando de JDK Unmodifiable*x goiaba Immutable*, na verdade a diferença também está no desempenho . Coleções imutáveis pode ser tanto mais rápido e mais eficiente para a memória se eles são não invólucros em torno coleções regulares (implementações JDK são wrappers). Citando a equipe da goiaba :

O JDK fornece métodos Collections.unmodifiableXXX, mas em nossa opinião, eles podem ser

<...>

  • ineficiente: as estruturas de dados ainda têm toda a sobrecarga de coleções mutáveis, incluindo verificações de modificação simultâneas, espaço extra em tabelas de hash, etc.
Dmide
fonte
pensando em desempenho, você também deve levar em consideração que um invólucro não modificável não copia a coleção, onde a versão imutável usada na goiaba e agora também no jdk9 + com, por exemplo List.of(...), copia duas vezes!
benez 24/09/18
2

Para citar os Tutoriais Java ™ :

Ao contrário dos wrappers de sincronização, que adicionam funcionalidade à coleção empacotada, os invólucros não modificáveis ​​retiram a funcionalidade. Em particular, eles eliminam a capacidade de modificar a coleção interceptando todas as operações que modificariam a coleção e lançando uma UnsupportedOperationException . Os invólucros não modificáveis ​​têm dois usos principais, a seguir:

  • Tornar uma coleção imutável depois de construída. Nesse caso, é uma boa prática não manter uma referência à coleção de backup. Isso absolutamente garante imutabilidade.

  • Permitir que determinados clientes tenham acesso somente leitura às suas estruturas de dados. Você mantém uma referência à coleção de backup, mas distribui uma referência ao wrapper. Dessa maneira, os clientes podem olhar, mas não podem modificar, enquanto você mantém acesso total.

(ênfase minha)

Isso realmente resume tudo.

Bharath
fonte
1

Como observado acima, não modificável não é imutável porque uma coleção não modificável pode ser alterada se, por exemplo, uma coleção não modificável tiver uma coleção delegada subjacente que é referenciada por outro objeto e esse objeto a altera.

Em relação ao imutável, nem está bem definido. No entanto, geralmente significa que o objeto "não será alterado", mas isso precisaria ser definido recursivamente. Por exemplo, eu posso definir imutável em classes cujas variáveis ​​de instância são todas primitivas e cujos métodos todos não contêm argumentos e retornam primitivas. Os métodos permitem recursivamente que as variáveis ​​da instância sejam imutáveis ​​e todos os métodos contenham argumentos que sejam imutáveis ​​e que retornem valores imutáveis. Os métodos devem ter a garantia de retornar o mesmo valor ao longo do tempo.

Supondo que possamos fazer isso, também existe o thread de conceito seguro. E você pode ser levado a acreditar que imutável (ou não mutável ao longo do tempo) também implica na segurança de threads. No entanto, esse não é o casoe esse é o ponto principal que estou apresentando aqui que ainda não foi mencionado em outras respostas. Posso construir um objeto imutável que sempre retorna os mesmos resultados, mas que não é seguro para threads. Para ver isso, suponha que eu construa uma coleção imutável mantendo adições e exclusões ao longo do tempo. Agora, a coleção imutável retorna seus elementos olhando para a coleção interna (que pode estar mudando com o tempo) e depois (internamente) adicionando e excluindo os elementos que foram adicionados ou excluídos após a criação da coleção. Claramente, embora a coleção sempre retorne os mesmos elementos, ela não é segura para threads apenas porque nunca alterará o valor.

Agora podemos definir imutáveis ​​como objetos que são seguros para threads e nunca serão alterados. Existem diretrizes para a criação de classes imutáveis ​​que geralmente levam a essas classes, no entanto, lembre-se de que pode haver maneiras de criar classes imutáveis, que requerem atenção à segurança do encadeamento, por exemplo, conforme descrito no exemplo da coleção "snapshot" acima.

dan b
fonte
1

Os tutoriais Java ™ dizem o seguinte:

Ao contrário dos wrappers de sincronização, que adicionam funcionalidade à coleção empacotada, os invólucros não modificáveis ​​retiram a funcionalidade. Em particular, eles eliminam a capacidade de modificar a coleção interceptando todas as operações que modificariam a coleção e lançando uma UnsupportedOperationException. Os invólucros não modificáveis ​​têm dois usos principais, a seguir:

Tornar uma coleção imutável depois de construída. Nesse caso, é uma boa prática não manter uma referência à coleção de backup. Isso absolutamente garante imutabilidade.

Permitir que determinados clientes tenham acesso somente leitura às suas estruturas de dados. Você mantém uma referência à coleção de backup, mas distribui uma referência ao wrapper. Dessa maneira, os clientes podem olhar, mas não podem modificar, enquanto você mantém acesso total.

Eu acho que é uma explicação boa o suficiente para entender a diferença.

piyush121
fonte