Неизменяемый объект - это объект, в котором внутренние поля (или, по крайней мере, все внутренние поля, которые влияют на его внешнее поведение) не могут быть изменены.
Есть много преимуществ для неизменяемых строк:
Производительность: выполните следующую операцию:
String substring = fullstring.substring(x,y);
Базовый C для метода substring (), вероятно, выглядит примерно так:
// 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;
}
Обратите внимание, что ни один из символов не должен быть скопирован! Если бы объект String был изменяемым (символы могли бы измениться позже), вам бы пришлось скопировать все символы, в противном случае изменения символов в подстроке будут отражены в другой строке позже.
Параллельность: если внутренняя структура неизменяемого объекта является действительной, она всегда будет действительной. Нет никаких шансов, что разные потоки могут создать недопустимое состояние в этом объекте. Следовательно, неизменяемые объекты являются потокобезопасными .
Сборка мусора: сборщику мусора гораздо проще принимать логические решения относительно неизменных объектов.
Однако есть и недостатки неизменности:
Производительность: Подожди, я думал, ты сказал, что производительность была неизменной! Ну, это иногда, но не всегда. Возьмите следующий код:
foo = foo.substring(0,4) + "a" + foo.substring(5); // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder
Обе строки заменяют четвертый символ буквой «а». Второй фрагмент кода не только более читабелен, но и быстрее. Посмотрите, как вам придется делать основной код для foo. Подстроки просты, но теперь, когда в пятой позиции уже есть символ и что-то еще может ссылаться на foo, вы не можете просто изменить его; Вы должны скопировать всю строку (конечно, некоторые из этих функций абстрагированы в функции в реальном базовом C, но суть здесь в том, чтобы показать код, который выполняется все в одном месте).
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));
Обратите внимание, что concatenate вызывается дважды, что означает, что вся строка должна быть зациклена! Сравните это с кодом C для bar
операции:
bar->characters[4] = 'a';
Операция с изменяемой строкой, очевидно, намного быстрее.
В заключение: в большинстве случаев вам нужна неизменная строка. Но если вам нужно много добавлять и вставлять в строку, вам нужна изменчивость для скорости. Если вы хотите использовать преимущества безопасности параллелизма и сборки мусора, ключом является сохранение ваших изменяемых объектов локальными для метода:
// 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();
}
Поскольку mutable
объект является локальной ссылкой, вам не нужно беспокоиться о безопасности параллелизма (когда-либо к нему обращается только один поток). А поскольку на него больше нигде нет ссылок, он размещается только в стеке, поэтому он освобождается после завершения вызова функции (вам не нужно беспокоиться о сборке мусора). И вы получаете все преимущества производительности как изменчивости, так и неизменности.