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 bar
operaçã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 mutable
objeto é 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.