O que se entende por imutável?

400

Esta poderia ser a pergunta mais idiota já feita, mas acho que é bastante confusa para um novato em Java.

  1. Alguém pode esclarecer o que se entende por imutável ?
  2. Por que é Stringimutável?
  3. Quais são as vantagens / desvantagens dos objetos imutáveis?
  4. Por que um objeto mutável, como o StringBuilderpreferido, é preferível a String e vice-verso?

Um bom exemplo (em Java) será realmente apreciado.

ashokgelal
fonte
73
Veja, não era uma pergunta tão idiota. Que bom que você perguntou!
DOK
2
By the way, eu não acho que é a pergunta mais idiota que nunca :) Eu acho que é um conceito muito importante entender
Jason Coco
11
Quando você disse StringBuilder, você não quis dizer a classe mutável StringBuffer? String e StringBuffer são mais semelhantes em função do que String e StringBuilder. StringBuffer é efetivamente uma string mutável.
Derek Mahar
3
Posso sugerir que adicionemos a tag "iniciante" a esta pergunta, para que programadores iniciantes em Java possam encontrá-la em busca de outras perguntas introdutórias?
Derek Mahar

Respostas:

268

Imutável significa que depois que o construtor de um objeto concluir a execução, a instância não poderá ser alterada.

Isso é útil, pois significa que você pode passar referências ao objeto, sem se preocupar com o fato de alguém mudar seu conteúdo. Especialmente ao lidar com simultaneidade, não há problemas de bloqueio com objetos que nunca mudam

por exemplo

class Foo
{
     private final String myvar;

     public Foo(final String initialValue)
     {
         this.myvar = initialValue;
     }

     public String getValue()
     {
         return this.myvar;
     }
}

Foonão precisa se preocupar que o chamador getValue()altere o texto na sequência.

Se você imaginar uma classe semelhante a Foo, mas com um membro em StringBuildervez de Stringmembro, poderá ver que um chamador getValue()poderá alterar o StringBuilderatributo de uma Fooinstância.

Também tenha cuidado com os diferentes tipos de imutabilidade que você pode encontrar: Eric Lippert escreveu um artigo de blog sobre isso. Basicamente, você pode ter objetos cuja interface é imutável, mas nos bastidores, o estado privado real dos mutables (e, portanto, não pode ser compartilhado com segurança entre os threads).

Douglas Leeder
fonte
3
Eu acho que você deve adicionar um construtor arg para atribuir valor pelo menos uma vez. O ponto do código atual não é claro, pois não há valor para alterar realmente :).
Georgy Bolyuba
4
Você deve tornar o campo somente leitura. Torna completamente explícito que o campo é imutável. No momento, é imutável por convenção
JaredPar 11/11/2008
7
O membro myVar deve ser final para que isso seja realmente imutável.
Laz
13
Você está certo de que o myVar não está acessível fora do Foo. No entanto, a presença de final indica para qualquer pessoa que possa estar modificando a classe no futuro que seu valor não deve mudar. Tendo a favor de ser o mais explícito possível em tais circunstâncias.
Laz
2
Que tal "Os tipos de referência não podem ser imutáveis ​​apenas usando a palavra-chave final. Final apenas impede a reatribuição". de en.wikipedia.org/wiki/Immutable_object
Yousha Aleayoub
81

Um objeto imutável é um objeto em que os campos internos (ou pelo menos todos os campos internos que afetam seu comportamento externo) não podem ser alterados.

Há muitas vantagens em cadeias imutáveis:

Desempenho: Execute a seguinte operação:

String substring = fullstring.substring(x,y);

O C subjacente para o método substring () é provavelmente algo como isto:

// Assume string is stored like this:
struct String { char* characters; unsigned int length; };

// Passing pointers because Java is pass-by-reference
struct String* substring(struct String* in, unsigned int begin, unsigned int end)
{
    struct String* out = malloc(sizeof(struct String));
    out->characters = in->characters + begin;
    out->length = end - begin;
    return out;
}

Observe que nenhum dos caracteres precisa ser copiado! Se o objeto String fosse mutável (os caracteres poderiam mudar mais tarde), você teria que copiar todos os caracteres, caso contrário, as alterações nos caracteres da substring serão refletidas na outra sequência posteriormente.

Simultaneidade: se a estrutura interna de um objeto imutável for válida, ela sempre será válida. Não há chance de que diferentes threads possam criar um estado inválido nesse objeto. Portanto, objetos imutáveis ​​são seguros para threads .

Coleta de lixo: é muito mais fácil para o coletor de lixo tomar decisões lógicas sobre objetos imutáveis.

No entanto, também existem desvantagens na imutabilidade:

Desempenho: espere, pensei que você dissesse que o desempenho era uma vantagem da imutabilidade! Bem, às vezes é, mas nem sempre. Pegue o seguinte código:

foo = foo.substring(0,4) + "a" + foo.substring(5);  // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder

As duas linhas substituem o quarto caractere pela letra "a". Não é apenas o segundo trecho de código mais legível, é mais rápido. Veja como você teria que fazer o código subjacente para foo. As substrings são fáceis, mas agora, como já existe um personagem no espaço cinco e outra coisa pode estar se referindo a foo, você não pode simplesmente mudá-lo; você precisa copiar toda a cadeia de caracteres (é claro que parte dessa funcionalidade é abstraída para funções no C subjacente real, mas o objetivo aqui é mostrar o código que é executado em um único local).

struct String* concatenate(struct String* first, struct String* second)
{
    struct String* new = malloc(sizeof(struct String));
    new->length = first->length + second->length;

    new->characters = malloc(new->length);

    int i;

    for(i = 0; i < first->length; i++)
        new->characters[i] = first->characters[i];

    for(; i - first->length < second->length; i++)
        new->characters[i] = second->characters[i - first->length];

    return new;
}

// The code that executes
struct String* astring;
char a = 'a';
astring->characters = &a;
astring->length = 1;
foo = concatenate(concatenate(slice(foo,0,4),astring),slice(foo,5,foo->length));

Observe que concatenar é chamado duas vezes, o que significa que toda a cadeia precisa ser repetida! Compare isso com o código C para a baroperação:

bar->characters[4] = 'a';

A operação de sequência mutável é obviamente muito mais rápida.

Conclusão: Na maioria dos casos, você deseja uma sequência imutável. Mas se você precisar acrescentar e inserir muito em uma string, precisará da mutabilidade para obter velocidade. Se você deseja os benefícios de segurança e coleta de lixo da concorrência, a chave é manter seus objetos mutáveis ​​locais para um método:

// This will have awful performance if you don't use mutable strings
String join(String[] strings, String separator)
{
    StringBuilder mutable;
    boolean first = true;

    for(int i = 0; i < strings.length; i++)
    {
        if(!first) first = false;
        else mutable.append(separator);

        mutable.append(strings[i]);
    }

    return mutable.toString();
}

Como o mutableobjeto é uma referência local, você não precisa se preocupar com a segurança de simultaneidade (apenas um encadeamento o toca). E como não é referenciado em nenhum outro lugar, ele é alocado apenas na pilha; portanto, é desalocado assim que a chamada de função é concluída (você não precisa se preocupar com a coleta de lixo). E você obtém todos os benefícios de desempenho de mutabilidade e imutabilidade.

Imagist
fonte
4
Ótima leitura! apenas uma coisa que eu acho que deveria ser se (primeiro) e não se (em primeiro lugar!)
Siddhartha
O que é necessário não é que os campos sejam imutáveis, mas que o estado observável definido do objeto seja imutável; um objeto que mantém uma referência a outro objeto como meio de encapsular o estado contido nele só pode ser imutável se todos os aspectos encapsulados do estado que ele expõe ao mundo exterior forem igualmente imutáveis. Observe que não é necessário nem suficiente que os campos sejam de tipos imutáveis. O que importa é o estado visível.
Supercat
7
Passing pointers because Java is pass-by-referenceJava não é "passagem por valor"?
Cristian Gutu
@CristianGutu sim você é JAVA direita é "passar por valor" não "passar por referência"
Arsh Kaushal
A referência é passada como o valor !!
Devv #
31

Na verdade, String não é imutável se você usar a definição da Wikipedia sugerida acima.

O estado da string muda após a construção. Dê uma olhada no método hashcode (). A cadeia armazena em cache o valor do código de hash em um campo local, mas não o calcula até a primeira chamada de hashcode (). Essa avaliação preguiçosa do código de hash coloca String em uma posição interessante como um objeto imutável cujo estado muda, mas não se pode observar que ele mudou sem o uso de reflexão.

Portanto, talvez a definição de imutável deva ser um objeto que não se pode observar ter mudado.

Se o estado muda em um objeto imutável após a criação, mas ninguém pode vê-lo (sem reflexão), o objeto ainda é imutável?


fonte
11
Boa ideia - um objeto que não pode ser observado como alterado, bem como, nenhuma maneira de alterá-lo do lado de fora. O campo privado para hashCode () é uma alteração interna que não é material para o estado visível externamente do objeto.
22410 mparaz
2
Na verdade, pode- se observar que ele mudou se você usar reflexão. Veja mais em Sedgewick's Strings são mutáveis ​​se você permitir a reflexão .
Miguel
24

Objetos imutáveis ​​são objetos que não podem ser alterados programaticamente. Eles são especialmente bons para ambientes multiencadeados ou outros ambientes em que mais de um processo é capaz de alterar (alterar) os valores em um objeto.

Apenas para esclarecer, no entanto, o StringBuilder é realmente um objeto mutável, não imutável. Uma String java regular é imutável (o que significa que, uma vez criada, você não pode alterar a string subjacente sem alterar o objeto).

Por exemplo, digamos que eu tenho uma classe chamada ColoredString que possui um valor String e uma cor String:

public class ColoredString {

    private String color;
    private String string;

    public ColoredString(String color, String string) {
        this.color  = color;
        this.string = string;
    }

    public String getColor()  { return this.color;  }
    public String getString() { return this.string; }

    public void setColor(String newColor) {
        this.color = newColor;
    }

}

Neste exemplo, o ColoredString é considerado mutável porque você pode alterar (alterar) uma de suas principais propriedades sem criar uma nova classe ColoredString. A razão pela qual isso pode ser ruim é, por exemplo, digamos que você tenha um aplicativo GUI com vários threads e esteja usando o ColoredStrings para imprimir dados na janela. Se você tiver uma instância do ColoredString que foi criada como

new ColoredString("Blue", "This is a blue string!");

Então você esperaria que a string sempre fosse "azul". Se outro encadeamento, no entanto, conseguiu acessar essa instância e chamou

blueString.setColor("Red");

Você de repente, e provavelmente inesperadamente, agora terá uma string "Vermelha" quando quiser uma "Azul". Por esse motivo, objetos imutáveis ​​são quase sempre preferidos ao passar instâncias de objetos. Quando você tem um caso em que objetos mutáveis ​​são realmente necessários, normalmente você protege o objeto apenas distribuindo cópias de seu campo de controle específico.

Para recapitular, em Java, java.lang.String é um objeto imutável (não pode ser alterado depois de criado) e java.lang.StringBuilder é um objeto mutável porque pode ser alterado sem a criação de uma nova instância.

Jason Coco
fonte
Você deve tornar os campos somente leitura. No momento, sua classe é imutável por convenção. Não há indicação para futuros desenvolvedores de que o imutável é intencional. Fazendo os campos somente leitura vai ajudar a esclarecer a sua intenção de um futuro dev
JaredPar
@JaredPar - Na verdade, a classe não é imutável ... é um exemplo de classe mutável para demonstrar por que pode ser um problema.
11138 Jason Coco
11
@JaredPar - Oh, está tudo bem :) Eu ia reescrever um pouco para ficar mais claro, mas o de Douglas já está bem escrito e parece ser o favorito, então vou deixar o meu como outro exemplo; mas alguém chegou a editá-lo para fazer as propriedades finais que achei que foi divertir :)
Jason Coco
24
  1. Em aplicativos grandes, é comum que os literais de string ocupem grandes bits de memória. Portanto, para lidar com a memória com eficiência, a JVM aloca uma área chamada "Pool constante de String". ( Observe que na memória, mesmo uma String não referenciada carrega um caractere [], um int pelo seu comprimento e outro pelo seu hashCode. , por outro lado, é necessário um máximo de oito bytes imediatos )
  2. Quando o complier se depara com um literal String, ele verifica o pool para ver se já existe um literal idêntico. E se for encontrado, a referência ao novo literal será direcionada para a String existente, e nenhum novo 'objeto literal da String' será criado (a String existente simplesmente obtém uma referência adicional).
  3. Portanto: a mutabilidade de string economiza memória ...
  4. Mas quando qualquer uma das variáveis ​​altera o valor, na verdade - apenas a referência é alterada, não o valor na memória (portanto, não afetará as outras variáveis ​​que a referenciam) como visto abaixo ...

String s1 = "String antiga";

//s1 variable, refers to string in memory
        reference                 |     MEMORY       |
        variables                 |                  |

           [s1]   --------------->|   "Old String"   |

String s2 = s1;

//s2 refers to same string as s1
                                  |                  |
           [s1]   --------------->|   "Old String"   |
           [s2]   ------------------------^

s1 = "Nova string";

//s1 deletes reference to old string and points to the newly created one
           [s1]   -----|--------->|   "New String"   |
                       |          |                  |
                       |~~~~~~~~~X|   "Old String"   |
           [s2]   ------------------------^

A string original 'in memory' não mudou, mas a variável de referência foi alterada para que se refira à nova string. E se não tivéssemos s2, "Old String" ainda estaria na memória, mas não poderemos acessá-lo ...

Shanaka Jayalath
fonte
16

"imutável" significa que você não pode alterar o valor. Se você tiver uma instância da classe String, qualquer método que você chamar que pareça modificar o valor criará outra String.

String foo = "Hello";
foo.substring(3);
<-- foo here still has the same value "Hello"

Para preservar as mudanças, você deve fazer algo como foo = foo.sustring (3);

Imutável vs mutável pode ser engraçado quando você trabalha com coleções. Pense no que acontecerá se você usar um objeto mutável como chave para o mapa e depois alterar o valor (dica: pense em equalse hashCode).

Georgy Bolyuba
fonte
13

java.time

Pode ser um pouco tarde, mas para entender o que é um objeto imutável, considere o seguinte exemplo da nova API Java 8 Date and Time ( java.time ). Como você provavelmente sabe que todos os objetos de data do Java 8 são imutáveis , no exemplo a seguir

LocalDate date = LocalDate.of(2014, 3, 18); 
date.plusYears(2);
System.out.println(date);

Resultado:

18/03/2014

Isso imprime o mesmo ano que a data inicial, pois plusYears(2)retorna um novo objeto, portanto a data antiga permanece inalterada por ser um objeto imutável. Depois de criada, você não pode modificá-la ainda mais e a variável de data ainda aponta para ela.

Portanto, esse exemplo de código deve capturar e usar o novo objeto instanciado e retornado por essa chamada para plusYears.

LocalDate date = LocalDate.of(2014, 3, 18); 
LocalDate dateAfterTwoYears = date.plusYears(2);

date.toString ()… 18/03/2014

dateAfterTwoYears.toString ()… 18/03/2016

George
fonte
8

Eu realmente gosto da explicação do SCJP Sun Certified Programmer para Java 5 Study Guide .

Para tornar o Java mais eficiente em memória, a JVM separa uma área especial de memória chamada "Pool constante de String". Quando o compilador encontra um literal String, ele verifica o pool para ver se já existe um String idêntico. Se uma correspondência for encontrada, a referência ao novo literal será direcionada para a String existente e nenhum novo objeto literal de String será criado.

wen
fonte
Deve ser capaz de fazer isso com objetos imutáveis ​​idênticos, mas suponho que isso levaria muito tempo de execução.
Zan Lynx
8

Objetos imutáveis ​​não podem ter seu estado alterado depois que eles foram criados.

Há três razões principais para usar objetos imutáveis ​​sempre que possível, o que ajudará a reduzir o número de bugs que você introduz no seu código:

  • É muito mais fácil argumentar sobre como seu programa funciona quando você sabe que o estado de um objeto não pode ser alterado por outro método
  • Objetos imutáveis ​​são automaticamente protegidos contra threads (supondo que sejam publicados com segurança), portanto nunca serão a causa desses erros de multithreading difíceis de identificar
  • Objetos imutáveis ​​sempre terão o mesmo código Hash, para que possam ser usados ​​como as chaves em um HashMap (ou similar). Se o código hash de um elemento em uma tabela hash fosse alterado, a entrada da tabela seria efetivamente perdida, pois as tentativas de encontrá-lo na tabela acabariam procurando no lugar errado. Esse é o principal motivo pelo qual os objetos String são imutáveis ​​- eles são freqüentemente usados ​​como chaves HashMap.

Também há outras otimizações que você poderá fazer no código quando souber que o estado de um objeto é imutável - armazenando em cache o hash calculado, por exemplo - mas essas são otimizações e, portanto, não são tão interessantes.

Bill Michell
fonte
5

Um significado tem a ver com a forma como o valor é armazenado no computador. Para uma string .Net, por exemplo, significa que a string na memória não pode ser alterada. Quando você pensa que está mudando, está criando um novo string na memória e apontando a variável existente (que é apenas um ponteiro para a coleção real de caracteres em outro lugar) para a nova string.

Charles Bretana
fonte
4
String s1="Hi";
String s2=s1;
s1="Bye";

System.out.println(s2); //Hi  (if String was mutable output would be: Bye)
System.out.println(s1); //Bye

s1="Hi": um objeto s1foi criado com o valor "Hi".

s2=s1 : um objeto s2é criado com referência ao objeto s1.

s1="Bye": o s1valor do objeto anterior não muda porque s1possui o tipo String e o tipo String é um tipo imutável. Em vez disso, o compilador cria um novo objeto String com o valor "Bye" e com s1referência a ele. aqui, quando imprimirmos o s2valor, o resultado será "Oi" e não "Tchau", porque relacionado s2ao s1objeto anterior que tinha valor "Oi".

kokabi
fonte
você pode adicionar uma pequena explicação, por favor?
Minigeek
3

Imutável significa que, uma vez que o objeto é criado, nenhum de seus membros será alterado. Stringé imutável, pois você não pode alterar seu conteúdo. Por exemplo:

String s1 = "  abc  ";
String s2 = s1.trim();

No código acima, a string s1 não mudou, outro objeto ( s2) foi criado usando s1.

eishay
fonte
3

Imutável significa simplesmente imutável ou não modificável. Depois que o objeto string é criado, seus dados ou estado não podem ser alterados

Considere o exemplo abaixo,

class Testimmutablestring{  
  public static void main(String args[]){  
    String s="Future";  
    s.concat(" World");//concat() method appends the string at the end  
    System.out.println(s);//will print Future because strings are immutable objects  
  }  
 }  

Vamos ter uma idéia considerando o diagrama abaixo,

insira a descrição da imagem aqui

Neste diagrama, você pode ver o novo objeto criado como "Mundo do Futuro". Mas não mude "Futuro". Because String is immutable. s, ainda consulte "Futuro". Se você precisar chamar "Mundo do Futuro",

String s="Future";  
s=s.concat(" World");  
System.out.println(s);//print Future World

Por que os objetos de string são imutáveis ​​em java?

Porque Java usa o conceito de string literal. Suponha que haja 5 variáveis ​​de referência, todas se referindo a um objeto "Futuro". Se uma variável de referência alterar o valor do objeto, ela será afetada por todas as variáveis ​​de referência. É por isso que os objetos string são imutáveis ​​em java.

JustCode
fonte
2

Uma vez instanciado, não pode ser alterado. Considere uma classe da qual uma instância possa ser usada como a chave para uma hashtable ou similar. Confira as melhores práticas de Java.

stjohnroe
fonte
0

Objetos imutáveis

Um objeto é considerado imutável se seu estado não puder ser alterado após a construção. A confiança máxima em objetos imutáveis ​​é amplamente aceita como uma estratégia sólida para criar código simples e confiável.

Objetos imutáveis ​​são particularmente úteis em aplicativos simultâneos. Como eles não podem mudar de estado, eles não podem ser corrompidos por interferência de thread ou observados em um estado inconsistente.

Os programadores costumam relutar em empregar objetos imutáveis, porque se preocupam com o custo de criar um novo objeto em vez de atualizar um objeto no local. O impacto da criação de objetos geralmente é superestimado e pode ser compensado por algumas das eficiências associadas a objetos imutáveis. Isso inclui uma sobrecarga reduzida devido à coleta de lixo e a eliminação do código necessário para proteger objetos mutáveis ​​da corrupção.

As subseções a seguir recebem uma classe cujas instâncias são mutáveis ​​e derivam uma classe com instâncias imutáveis. Ao fazer isso, eles fornecem regras gerais para esse tipo de conversão e demonstram algumas das vantagens de objetos imutáveis.

Fonte

Kamal Singh
fonte
0

Como a resposta aceita não responde a todas as perguntas. Sou forçado a dar uma resposta após 11 anos e 6 meses.

Alguém pode esclarecer o que se entende por imutável?

Espero que você tenha entendido um objeto imutável (porque poderíamos pensar em referências imutáveis ).

Um objeto é imutável : se criado, eles sempre representam o mesmo valor (não possui nenhum método que altere o valor).

Por que é Stringimutável?

Respeite a definição acima, que pode ser verificada no código-fonte Sting.java .

Quais são as vantagens / desvantagens dos objetos imutáveis? tipos imutáveis ​​são:

  • mais seguro de bugs.

  • mais fácil de entender.

  • e mais pronto para a mudança.

Por que um objeto mutável, como StringBuilder, deve ter preferência sobre String e vice-verso?

Limitando a pergunta Por que precisamos do StringBuilder mutável na programação? Um uso comum para isso é concatenar um grande número de seqüências de caracteres juntas, assim:

String s = "";
for (int i = 0; i < n; ++i) {
    s = s + n;
}

Usando cadeias imutáveis, isso faz muitas cópias temporárias - o primeiro número da cadeia ("0") é realmente copiado n vezes no decorrer da construção da cadeia final, o segundo número é copiado n-1 vezes e, portanto, em. Na verdade, custa O (n2) tempo apenas para fazer toda essa cópia, mesmo que apenas concatenássemos n elementos.

O StringBuilder foi projetado para minimizar essa cópia. Ele usa uma estrutura de dados interna simples, mas inteligente, para evitar copiar até o final, quando você solicita a String final com uma chamada toString ():

StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; ++i) {
  sb.append(String.valueOf(n));
}
String s = sb.toString();

Obter um bom desempenho é uma das razões pelas quais usamos objetos mutáveis. Outra é o compartilhamento conveniente: duas partes do seu programa podem se comunicar de maneira mais conveniente, compartilhando uma estrutura de dados mutável comum.

Mais informações podem ser encontradas aqui: https://web.mit.edu/6.005/www/fa15/classes/09-immutability/#useful_immutable_types

Chulo
fonte
-1

Um objeto imutável é aquele que você não pode modificar depois de criá-lo. Um exemplo típico são literais de string.

A linguagem de programação AD, que se torna cada vez mais popular, tem uma noção de "imutabilidade" por meio da palavra-chave "invariável". Verifique o artigo deste Dr.Dobb sobre isso - http://dobbscodetalk.com/index.php?option=com_myblog&show=Invariant-Strings.html&Itemid=29 . Isso explica o problema perfeitamente.

user36393
fonte
Eu acredito que, a partir do D 2.020, a palavra-chave foi alterada de invariável para imutável. Não vejo razão, mas diz "imutável agora está implementado". digitalmars.com/d/2.0/changelog.html#new2_020
he_the_great 11/11/2008