Por que String é imutável em Java?

78

Eu não conseguia entender o motivo disso. Eu sempre uso a classe String como outros desenvolvedores, mas quando modifico o valor dela, é criada uma nova instância da String.

Qual pode ser o motivo da imutabilidade da classe String em Java?

Eu sei que existem algumas alternativas como StringBuffer ou StringBuilder. É apenas curiosidade.

yfklon
fonte
20
Tecnicamente, não é uma duplicata, mas Eric Lippert dá uma grande resposta a esta pergunta aqui: programmers.stackexchange.com/a/190913/33843
Heinzi

Respostas:

105

Concorrência

Java foi definido desde o início com considerações de simultaneidade. Como frequentemente mencionado, os mutáveis ​​compartilhados são problemáticos. Uma coisa pode mudar outra atrás da parte traseira de outro encadeamento sem que esse encadeamento esteja ciente disso.

Há uma série de erros de C ++ multithread que surgiram por causa de uma string compartilhada - onde um módulo achou seguro mudar quando outro módulo no código salvou um ponteiro e esperava que permanecesse o mesmo.

A 'solução' para isso é que toda classe faz uma cópia defensiva dos objetos mutáveis ​​que são passados ​​para ela. Para cadeias mutáveis, é O (n) para fazer a cópia. Para seqüências imutáveis, fazer uma cópia é O (1) porque não é uma cópia, é o mesmo objeto que não pode ser alterado.

Em um ambiente multithread, objetos imutáveis ​​sempre podem ser compartilhados com segurança entre si. Isso leva a uma redução geral no uso de memória e melhora o cache de memória.

Segurança

Muitas vezes, as strings são passadas como argumentos para os construtores - conexões de rede e protocolos são os dois que mais facilmente vêm à mente. Ser capaz de alterar isso em um tempo indeterminado posteriormente na execução pode levar a problemas de segurança (a função pensou que estava se conectando a uma máquina, mas foi desviada para outra, mas tudo no objeto parece estar conectado à primeira ... é até a mesma string).

Java permite usar a reflexão - e os parâmetros para isso são cadeias de caracteres. O perigo de alguém passar uma string que pode ser modificada pelo caminho para outro método que reflete. Isso é muito ruim.

Chaves para o Hash

A tabela de hash é uma das estruturas de dados mais usadas. As chaves da estrutura de dados são muitas vezes cadeias de caracteres. Ter cadeias imutáveis ​​significa que (como acima) a tabela de hash não precisa fazer uma cópia da chave de hash a cada vez. Se as strings fossem mutáveis ​​e a tabela de hash não fizesse isso, seria possível que algo alterasse a chave de hash à distância.

A maneira como o Objeto em java funciona é que tudo tem uma chave de hash (acessada pelo método hashCode ()). Ter uma sequência imutável significa que o hashCode pode ser armazenado em cache. Considerando a frequência com que Strings são usadas como chaves para um hash, isso fornece um aumento significativo no desempenho (em vez de ter que recalcular o código de hash toda vez).

Substrings

Por ter a String imutável, a matriz de caracteres subjacente que suporta a estrutura de dados também é imutável. Isso permite certas otimizações no substringmétodo a ser feito (elas não são necessariamente feitas - também introduz a possibilidade de alguns vazamentos de memória).

Se você fizer:

String foo = "smiles";
String bar = foo.substring(1,5);

O valor de baré 'milha'. No entanto, ambos fooe barpodem ser apoiados pela mesma matriz de caracteres, reduzindo a instanciação de mais matrizes de caracteres ou copiando-as - apenas usando diferentes pontos de início e de final na sequência.

foo | | (0, 6)
    vv
    sorrisos
     ^ ^
bar | | (1, 5)

Agora, a desvantagem disso (o vazamento de memória) é que, se alguém tivesse uma sequência de 1k de comprimento e pegasse a substring do primeiro e do segundo caracteres, ele também seria apoiado pela matriz de caracteres de 1k de comprimento. Essa matriz permaneceria na memória mesmo se a cadeia original que tivesse um valor de toda a matriz de caracteres fosse coletada como lixo.

Pode-se ver isso em String do JDK 6b14 (o código a seguir é de uma fonte GPL v2 e é usado como exemplo)

   public String(char value[], int offset, int count) {
       if (offset < 0) {
           throw new StringIndexOutOfBoundsException(offset);
       }
       if (count < 0) {
           throw new StringIndexOutOfBoundsException(count);
       }
       // Note: offset or count might be near -1>>>1.
       if (offset > value.length - count) {
           throw new StringIndexOutOfBoundsException(offset + count);
       }
       this.offset = 0;
       this.count = count;
       this.value = Arrays.copyOfRange(value, offset, offset+count);
   }

   // Package private constructor which shares value array for speed.
   String(int offset, int count, char value[]) {
       this.value = value;
       this.offset = offset;
       this.count = count;
   }

   public String substring(int beginIndex, int endIndex) {
       if (beginIndex < 0) {
           throw new StringIndexOutOfBoundsException(beginIndex);
       }
       if (endIndex > count) {
           throw new StringIndexOutOfBoundsException(endIndex);
       }
       if (beginIndex > endIndex) {
           throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
       }
       return ((beginIndex == 0) && (endIndex == count)) ? this :
           new String(offset + beginIndex, endIndex - beginIndex, value);
   }

Observe como a substring usa o construtor String no nível do pacote que não envolve nenhuma cópia da matriz e seria muito mais rápido (às custas de possivelmente manter algumas matrizes grandes - embora também não duplicando matrizes grandes).

Observe que o código acima é para Java 1.6. A maneira como o construtor de substring é implementado foi alterado no Java 1.7, conforme documentado em Alterações na representação interna da String feita no Java 1.7.0_06 - o problema relacionado ao vazamento de memória que mencionei acima. Provavelmente o Java não era visto como uma linguagem com muita manipulação de String e, portanto, o aumento de desempenho para uma substring foi uma coisa boa. Agora, com enormes documentos XML armazenados em seqüências de caracteres que nunca são coletadas, isso se torna um problema ... e, portanto, a mudança para Stringnão usar a mesma matriz subjacente com uma substring, para que a matriz de caracteres maior possa ser coletada mais rapidamente.

Não abuse da pilha

Um poderia passar o valor da corda em torno em vez da referência para a seqüência imutável para evitar problemas com a mutabilidade. No entanto, com cadeias grandes, passar isso na pilha seria ... abusivo para o sistema (colocar documentos xml inteiros como cadeias na pilha e depois retirá-los ou continuar a transmiti-los ...).

A possibilidade de desduplicação

É verdade que essa não foi uma motivação inicial para o motivo pelo qual as Strings deveriam ser imutáveis, mas quando se olha para o racional do motivo pelo qual as Strings imutáveis ​​são boas, certamente é algo a considerar.

Qualquer um que tenha trabalhado um pouco com Strings sabe que pode sugar memória. Isso é especialmente verdade quando você faz coisas como extrair dados de bancos de dados que ficam por um tempo. Muitas vezes com essas picadas, elas são a mesma string repetidamente (uma vez para cada linha).

Atualmente, muitos aplicativos Java de larga escala estão com gargalo na memória. As medidas mostraram que aproximadamente 25% dos dados dinâmicos do heap Java nesses tipos de aplicativos são consumidos pelos objetos String. Além disso, aproximadamente metade desses objetos String são duplicados, onde duplicados significa que string1.equals (string2) é verdadeira. Ter objetos String duplicados no heap é, essencialmente, apenas um desperdício de memória. ...

Com a atualização 20 do Java 8, o JEP 192 (motivação citada acima) está sendo implementado para resolver isso. Sem entrar em detalhes de como a desduplicação de string funciona, é essencial que as próprias Strings sejam imutáveis. Você não pode deduplicar StringBuilders porque eles podem mudar e você não quer que alguém mude algo de baixo de você. Seqüências imutáveis ​​(relacionadas a esse conjunto de cadeias) significam que você pode passar e, se encontrar duas cadeias iguais, pode apontar uma referência para outra e deixar que o coletor de lixo consuma o novo não utilizado.

Outras línguas

O objetivo C (que antecede Java) possui NSStringe NSMutableString.

C # e .NET fizeram as mesmas escolhas de design da string padrão sendo imutável.

As strings de Lua também são imutáveis.

Python também.

Historicamente, Lisp, Scheme, Smalltalk todos internam a string e, portanto, fazem com que ela seja imutável. As linguagens dinâmicas mais modernas costumam usar strings de alguma maneira que exige que sejam imutáveis ​​(pode não ser uma String , mas é imutável).

Conclusão

Essas considerações de design foram feitas repetidamente em vários idiomas. É o consenso geral de que seqüências imutáveis, apesar de todo o embaraço, são melhores que as alternativas e levam a um código melhor (menos bugs) e a executáveis ​​mais rápidos em geral.


fonte
3
Java fornece seqüências mutáveis ​​e imutáveis. Esta resposta detalha algumas vantagens de desempenho que podem ser feitas em seqüências imutáveis ​​e algumas razões pelas quais se pode escolher dados imutáveis; mas não discute por que a versão imutável é a versão padrão.
Billy ONeal
3
@ Billyilly: um padrão seguro e uma alternativa insegura quase sempre levam a sistemas mais seguros do que a abordagem oposta.
Joachim Sauer
4
@BillyONeal Se o imutável não fosse o padrão, os problemas de concorrência, segurança e hashes seriam mais comuns. Os designers de idiomas escolheram (em parte em resposta a C) criar um idioma em que os padrões sejam configurados para tentar evitar vários bugs comuns para tentar melhorar a eficiência do programador (sem ter que se preocupar mais com esses bugs). Existem menos erros (óbvios e ocultos) com seqüências imutáveis ​​do que com mutáveis.
@ Joachim: Eu não estou reivindicando o contrário.
Billy ONeal
1
Tecnicamente, o Common Lisp possui cadeias de caracteres mutáveis, para operações "semelhantes a cadeias de caracteres" e símbolos com nomes imutáveis ​​para identificadores imutáveis.
Vatine 22/07/2015
21

Razões pelas quais me lembro:

  1. O recurso Pool de strings sem tornar a string imutável não é possível porque, no caso de um pool de strings, um objeto / literal de string, por exemplo, "XYZ" será referenciado por muitas variáveis ​​de referência, portanto, se alguma delas alterar o valor, outras serão automaticamente afetadas .

  2. String tem sido amplamente usada como parâmetro para muitas classes java, por exemplo, para abrir conexão de rede, abrir conexão de banco de dados, abrir arquivos. Se String não for imutável, isso levaria a sérias ameaças à segurança.

  3. A imutabilidade permite que String armazene em cache seu código de hash.

  4. Torna a thread segura.

NINCOMPOOP
fonte
7

1) Pool de Cordas

O designer de Java sabe que o String será o tipo de dados mais usado em todos os tipos de aplicativos Java e é por isso que eles queriam otimizar desde o início. Um dos principais passos nessa direção foi a idéia de armazenar literais de String no pool de String. O objetivo era reduzir o objeto String temporário compartilhando-os e, para compartilhar, eles precisam ser da classe Immutable. Você não pode compartilhar um objeto mutável com duas partes que são desconhecidas uma pela outra. Vamos dar um exemplo hipotético, em que duas variáveis ​​de referência estão apontando para o mesmo objeto String:

String s1 = "Java";
String s2 = "Java";

Agora, se s1 mudar o objeto de "Java" para "C ++", a variável de referência também terá o valor s2 = "C ++", que ele nem conhece. Ao tornar String imutável, esse compartilhamento de String literal era possível. Em resumo, a ideia principal do pool de String não pode ser implementada sem tornar String final ou Imutável em Java.

2) Segurança

O Java tem um objetivo claro em termos de fornecer um ambiente seguro em todos os níveis de serviço e o String é crítico em todos os itens de segurança. String tem sido amplamente utilizado como parâmetro para muitas classes Java, por exemplo, para abrir conexão de rede, você pode passar host e porta como String, para ler arquivos em Java, pode passar caminho de arquivos e diretório como String e para abrir conexão de banco de dados, você pode passe o URL do banco de dados como String. Se String não for imutável, um usuário poderá ter concedido para acessar um arquivo específico no sistema, mas após a autenticação, ele poderá alterar o PATH para outra coisa, isso poderá causar sérios problemas de segurança. Da mesma forma, ao conectar-se ao banco de dados ou a qualquer outra máquina na rede, a mutação do valor da String pode representar ameaças à segurança. Strings mutáveis ​​também podem causar problemas de segurança no Reflection,

3) Uso de String no Mecanismo de Carregamento de Classes

Outro motivo para tornar a String final ou Imutável foi motivada pelo fato de ser muito usada no mecanismo de carregamento de classes. Como String não foi imutável, um invasor pode tirar proveito desse fato e uma solicitação para carregar classes Java padrão, por exemplo, java.io.Reader pode ser alterada para classe maliciosa com.unknown.DataStolenReader. Mantendo String final e imutável, podemos pelo menos ter certeza de que a JVM está carregando as classes corretas.

4) Benefícios Multithreading

Como a simultaneidade e a multithreading eram as principais ofertas do Java, fazia muito sentido pensar na segurança de threads dos objetos String. Como era esperado que o String fosse amplamente utilizado, tornando-o Imutável significa que não há sincronização externa, significa um código muito mais limpo envolvendo o compartilhamento do String entre vários threads. Esse recurso único já torna muito mais fácil a codificação de concorrência complicada, confusa e propensa a erros. Como String é imutável e apenas a compartilhamos entre threads, resulta em um código mais legível.

5) Otimização e desempenho

Agora, quando você torna uma classe Imutável, você sabe de antemão que essa classe não será alterada depois de criada. Isso garante um caminho aberto para muitas otimizações de desempenho, por exemplo, cache. A própria String sabe que, eu não vou mudar, portanto, a String armazena em cache seu código de hash. Ele ainda calcula o código hash preguiçosamente e, uma vez criado, apenas o armazena em cache. No mundo simples, quando você chama pela primeira vez o método hashCode () de qualquer objeto String, ele calcula o código de hash e todas as chamadas subseqüentes a hashCode () retornam o valor em cache já calculado. Isso resulta em um bom ganho de desempenho, dado que o String é muito usado em mapas baseados em hash, por exemplo, Hashtable e HashMap. O armazenamento em cache do hashcode não foi possível sem torná-lo imutável e final, pois depende do conteúdo do próprio String.

saidesh kilaru
fonte
5

A Java Virtual Machine executa várias otimizações em relação às operações de sequência que não poderiam ser executadas de outra forma. Por exemplo, se você tivesse uma sequência com o valor "Mississippi" e designasse "Mississippi" .substring (0, 4) para outra sequência, tanto quanto você sabe, uma cópia foi feita dos quatro primeiros caracteres para fazer "Miss" . O que você não sabe é que ambos compartilham a mesma string original "Mississippi", sendo um proprietário e o outro uma referência dessa string da posição 0 a 4. (A referência ao proprietário impede que o proprietário seja coletado por o coletor de lixo quando o proprietário sai do escopo)

Isso é trivial para uma cadeia de caracteres tão pequena quanto "Mississippi", mas com cadeias de caracteres maiores e várias operações, não ter que copiá-la economiza muito tempo! Se as strings fossem mutáveis, não seria possível fazer isso, pois a modificação do original afetaria as "cópias" da substring também.

Além disso, como Donal menciona, a vantagem seria muito reduzida pela sua desvantagem. Imagine que você escreva um programa que depende de uma biblioteca e use uma função que retorne uma string. Como você pode ter certeza de que esse valor permanecerá constante? Para garantir que isso não aconteça, você sempre precisará produzir uma cópia.

E se você tiver dois threads compartilhando a mesma string? Você não gostaria de ler uma sequência que está sendo reescrita por outro segmento, gostaria? Portanto, a string teria que ser segura para threads, que, sendo a classe comum, tornaria praticamente todos os programas Java muito mais lentos. Caso contrário, você teria que fazer uma cópia para cada encadeamento que requer essa sequência ou colocar o código usando essa sequência em um bloco de sincronização, os quais apenas tornam o programa mais lento.

Por todas essas razões, essa foi uma das primeiras decisões tomadas para Java para se diferenciar do C ++.

Neil
fonte
Teoricamente, você pode fazer um gerenciamento de buffer de várias camadas que permita copiar na mutação se compartilhado, mas isso é muito difícil de trabalhar com eficiência em um ambiente com vários threads.
Donal Fellows
@DonalFellows Acabei de assumir que, como a Java Virtual Machine não é escrita em Java (obviamente), ela é gerenciada internamente usando ponteiros compartilhados ou algo parecido.
31513 Neil
5

O motivo da imutabilidade da string vem da consistência com outros tipos primitivos no idioma. Se você possui um que intcontém o valor 42 e adiciona o valor 1 a ele, não altera o 42. Você obtém um novo valor, 43, que não tem nenhuma relação com os valores iniciais. Primitivas mutantes que não sejam cordas não fazem sentido conceitual; e, como tais, programas que tratam as cordas como imutáveis ​​costumam ser mais fáceis de raciocinar e entender.

Além disso, o Java realmente fornece seqüências mutáveis ​​e imutáveis, como você vê StringBuilder; na verdade, apenas o padrão é a sequência imutável. Se você deseja passar referências a StringBuildertodos os lugares, é perfeitamente bem-vindo. Java usa tipos separados ( Stringe StringBuilder) para esses conceitos porque não possui suporte para expressar mutabilidade ou a falta dela em seu sistema de tipos. Nas linguagens que oferecem suporte à imutabilidade em seus sistemas de tipos (por exemplo, C ++ const), geralmente existe um tipo de cadeia única que serve para ambos os propósitos.

Sim, ter uma sequência imutável permite implementar algumas otimizações específicas para sequências imutáveis, como internação, e permite passar referências de sequência sem sincronização entre os threads. No entanto, isso confunde o mecanismo com o objetivo pretendido de uma linguagem com um sistema de tipos simples e consistente. Eu comparo isso a como todos pensam na coleta de lixo da maneira errada; coleta de lixo não é "recuperação de memória não utilizada"; está "simulando um computador com memória ilimitada" . As otimizações de desempenho discutidas são coisas que são feitas para fazer com que o objetivo de seqüências imutáveis ​​funcione bem em máquinas reais; não é a razão de tais seqüências serem imutáveis ​​em primeiro lugar.

Billy ONeal
fonte
@ Billy-Oneal .. Em relação a "Se você tem um int contendo o valor 42 e adiciona o valor 1 a ele, não altera o 42. Você obtém um novo valor, 43, que não tem nenhuma relação com o início. valores ". Você tem certeza sobre isso?
Shamit Verma
@ Shamit: Sim, tenho certeza. Adicionando 1 a 42 resultados em 43. Não faz o número 42 significa a mesma coisa como o número 43.
Billy ONeal
@Shamit: Da mesma forma, você não pode fazer algo assim 43 = 6e esperar que o número 43 significa a mesma coisa que o número 6.
Billy ONeal
int i = 42; i = i + 1; este código irá armazenar 42 na memória e, em seguida, alterar os valores no mesmo local a 43. Então, na verdade, variável "i" adquire um novo valor de 43.
Shamit Verma
@ Shamit: Nesse caso, você mudou i, não 42. Considere string s = "Hello "; s += "World";. Você transformou o valor da variável s. Mas as cordas "Hello ", "World"e "Hello World"são imutáveis.
Billy ONeal
4

Imutabilidade significa que as constantes mantidas por classes que você não possui não podem ser modificadas. As classes que você não possui incluem as que estão no centro da implementação do Java, e as seqüências que não devem ser modificadas incluem itens como tokens de segurança, endereços de serviço etc. Você realmente não deve poder modificar esses tipos de coisas (e isso se aplica duplamente ao operar no modo em área restrita).

Se a String não fosse imutável, toda vez que você a recuperasse de algum contexto que não desejasse que o conteúdo da string fosse alterado, seria necessário fazer uma cópia "por precaução". Isso fica muito caro.

Donal Fellows
fonte
4
Esse mesmo argumento exato se aplica a qualquer tipo, não apenas a String. Mas, por exemplo, Arrays são mutáveis, no entanto. Então, por que são Stringimutáveis ​​e Arraynão são? E se a imutabilidade é tão importante, por que o Java torna tão difícil criar e trabalhar com objetos imutáveis?
Jörg W Mittag
1
@ JörgWMittag: Suponho que seja basicamente uma questão de quão radical eles queriam ser. Ter uma String imutável era bastante radical, nos dias Java 1.0. Ter uma estrutura de coleção imutável (primariamente ou mesmo exclusivamente) também poderia ter sido radical demais para obter amplo uso do idioma.
Joachim Sauer
Criar uma estrutura eficaz de coleções imutáveis ​​é bastante complicado de se obter, falando como alguém que escreveu isso (mas não em Java). Também desejo totalmente ter matrizes imutáveis; isso teria me poupado bastante trabalho.
Donal Fellows
@DonalFellows: o pcollections tem como objetivo fazer exatamente isso (nunca usei sozinho, no entanto).
Joachim Sauer
3
@ JörgWMittag: Existem pessoas (geralmente de uma perspectiva puramente funcional) que argumentam que todos os tipos devem ser imutáveis. Da mesma forma, acho que, se você adicionar todos os problemas relacionados ao trabalho com o estado mutável em software paralelo e simultâneo, poderá concordar que trabalhar com objetos imutáveis ​​geralmente é muito mais fácil do que os mutáveis.
precisa
2

Imagine um sistema em que você aceite alguns dados, verifique sua correção e os repasse (para serem armazenados em um banco de dados, por exemplo).

Supondo que os dados sejam a Stringe tenham que ter pelo menos 5 caracteres. Seu método se parece com isso:

public void handle(String input) {
  if (input.length() < 5) {
    throw new IllegalArgumentException();
  }
  storeInDatabase(input);
}

Agora podemos concordar que quando storeInDatabaseé chamado aqui, ele inputatenderá ao requisito. Mas se Stringfosse mutável, o chamador poderá alterar o inputobjeto (de outro encadeamento) logo após ter sido verificado e antes de ser armazenado no banco de dados . Isso exigiria um bom timing e provavelmente não iria bem todas as vezes, mas, ocasionalmente, ele conseguiria que você armazenasse valores inválidos no banco de dados.

Os tipos de dados imutáveis ​​são uma solução muito simples para esses problemas (e muitos outros relacionados): sempre que você verifica algum valor, pode depender do fato de que a condição verificada ainda é verdadeira posteriormente.

Joachim Sauer
fonte
Obrigada pelo esclarecimento. E se eu chamar o método handle como este; handle (nova String (entrada + "naberlan")). Eu acho que posso armazenar valores inválidos no db como este.
yfklon
1
@blank: bem, uma vez que o inputdo handlemétodo já é muito longo (não importa qual a origem input é), seria simplesmente lançar uma exceção. Você está criando uma nova entrada antes de chamar o método. Isso não é problema.
Joachim Sauer
0

Em geral, você encontrará tipos de valor e tipos de referência . Com um tipo de valor, você não se importa com o objeto que o representa, mas com o valor. Se eu lhe der um valor, você espera que esse valor permaneça o mesmo. Você não quer que isso mude de repente. O número 5 é um valor. Você não espera que mude para 6 de repente. A cadeia "Hello" é um valor. Você não espera que mude para "P *** off" de repente.

Com os tipos de referência, você se preocupa com o objeto e espera que ele mude. Por exemplo, você normalmente espera que uma matriz mude. Se eu lhe der uma matriz e você quiser mantê-la exatamente como é, ou você precisa confiar em mim para não alterá-la ou fazer uma cópia dela.

Com a classe Java string, os designers tiveram que tomar uma decisão: é melhor se as strings se comportarem como um tipo de valor ou se comportarem como um tipo de referência? No caso de sequências Java, foi tomada a decisão de que eles devem ser tipos de valor, o que significa que, como são objetos, devem ser objetos imutáveis.

A decisão oposta poderia ter sido tomada, mas na minha opinião teria causado muitas dores de cabeça. Como já foi dito, muitas línguas tomaram a mesma decisão e chegaram à mesma conclusão. Uma exceção é o C ++, que possui uma classe de cadeia, e as cadeias podem ser constantes ou não constantes, mas no C ++, diferentemente do Java, os parâmetros do objeto podem ser passados ​​como valores e não como referências.

gnasher729
fonte
0

Estou realmente surpreso que ninguém tenha apontado isso.

Resposta: Não o beneficiaria significativamente, mesmo que fosse mutável. Não o beneficiaria tanto quanto isso causa problemas adicionais. Vamos examinar dois casos mais comuns de mutação:

Alterando um caractere de uma sequência

Como cada caractere em uma string Java ocupa 2 ou 4 bytes, pergunte a si mesmo: você obteria alguma coisa se pudesse modificar a cópia existente?

No cenário em que você está substituindo um caractere de 2 bytes por 4 um (ou vice-versa), é necessário mudar a parte restante da string em 2 bytes para a esquerda ou para a direita. O que não é tão diferente de copiar toda a cadeia do ponto de vista computacional.

Esse também é um comportamento realmente irregular, geralmente indesejável. Imagine alguém testando um aplicativo com texto em inglês e, quando o aplicativo é adotado em países estrangeiros, como a China, tudo começa a funcionar de maneira estranha.

Anexando outra string (ou caractere) à existente

Se você tiver duas seqüências arbitrárias, elas ficam em dois locais de memória distintos. Se você deseja alterar o primeiro anexando o segundo, não é possível solicitar apenas memória adicional no final da primeira sequência, pois provavelmente ela já está ocupada.

É necessário copiar a sequência concatenada para um novo local, que é exatamente o mesmo como se as duas sequências fossem imutáveis.

Se você deseja anexar com eficiência, convém usar StringBuilder, o que reserva uma quantidade considerável de espaço no final de uma string, apenas para esse propósito de um possível anexo futuro.

Rok Kralj
fonte
-2
  1. elas são caras e mantê-las imutáveis ​​permite coisas como sub-strings que compartilham a matriz de bytes da string principal. (aumento de velocidade também, pois não é necessário criar uma nova matriz de bytes e copiar)

  2. segurança - não gostaria que seu código de pacote ou classe fosse renomeado

    [removeu o antigo 3 olhou o StringBuilder src - ele não compartilha memória com a string (até ser modificada) acho que estava na versão 1.3 ou 1.4]

  3. cache hashcode

  4. para seqüências mutáveis, use SB (construtor ou buffer, conforme necessário)

tgkprog
fonte
2
1. Obviamente, existe a penalidade de não poder destruir as partes maiores da corda, se isso acontecer. Internar não é gratuito; embora melhore o desempenho de muitos programas do mundo real. 2. Pode haver facilmente "string" e "ImmutableString" que atendam a esse requisito. 3. Eu não tenho certeza se entendi que ...
Billy ONeal
.3 deveria ter sido o cache do código hash. Isso também pode ser feito com uma string mutável. @ billy-oneal
tgkprog
-4

Strings deveriam ter sido um tipo de dados primitivo em Java. Se existissem, as strings seriam padronizadas como mutáveis ​​ea palavra-chave final geraria strings imutáveis. Seqüências mutáveis ​​são úteis e, portanto, existem vários hacks para sequências mutáveis ​​nas classes stringbuffer, stringbuilder e charsequence.

CWallach
fonte
3
Isso não responde ao aspecto "por que" do que é agora que a pergunta é feita. Além disso, o java final não funciona dessa maneira. Strings mutáveis ​​não são hacks, mas considerações reais de design com base nos usos mais comuns de strings e nas otimizações que podem ser feitas para melhorar a jvm.
1
A resposta para o "porquê" é uma má decisão de design de linguagem. Três maneiras ligeiramente diferentes de oferecer suporte a seqüências mutáveis ​​é um hack que o compilador / JVM deve manipular.
CWallach
3
String e StringBuffer eram os originais. StringBuilder foi adicionado posteriormente, reconhecendo uma dificuldade de design com o StringBuffer. Seqüências de caracteres mutáveis ​​e imutáveis, sendo objetos diferentes, são encontradas em muitas linguagens, já que a consideração do design foi feita repetidas vezes e decidimos que cada uma delas é um objeto diferente a cada vez. C # "As strings são imutáveis" e Por que o .NET String é imutável? , o objetivo C NSString é imutável enquanto NSMutableString é mutável. stackoverflow.com/questions/9544182