Почему String неизменяем в Java?

78

Я не мог понять причину этого. Я всегда использую класс String, как и другие разработчики, но когда я изменяю его значение, создается новый экземпляр String.

В чем может быть причина неизменности класса String в Java?

Я знаю, что есть некоторые альтернативы, такие как StringBuffer или StringBuilder. Это просто любопытство.

yfklon
источник
20
Технически, это не дубликат, но Эрик Липперт дает отличный ответ на этот вопрос здесь: programmers.stackexchange.com/a/190913/33843
Хайнци

Ответы:

105

совпадение

Java была определена с самого начала из соображений параллелизма. Как часто упоминалось, общие переменные являются проблематичными. Одна вещь может изменить другую за спиной другого потока без того, чтобы этот поток знал об этом.

Существует множество многопоточных ошибок C ++, которые возникли из-за общей строки - когда один модуль думал, что его можно безопасно изменить, когда другой модуль в коде сохранил указатель на него, и ожидал, что он останется прежним.

«Решение» этого заключается в том, что каждый класс создает защитную копию изменяемых объектов, которые ему передаются. Для изменяемых строк это O (n), чтобы сделать копию. Для неизменяемых строк создание копии - это O (1), потому что это не копия, а тот же объект, который не может измениться.

В многопоточной среде неизменные объекты всегда можно безопасно разделить между собой. Это приводит к общему снижению использования памяти и улучшает кеширование памяти.

Безопасность

Часто в качестве аргументов для конструкторов передаются строки - сетевые соединения и протоколы - два, которые легче всего приходят на ум. Возможность изменить это в неопределенное время позже при выполнении может привести к проблемам с безопасностью (функция думала, что она подключалась к одной машине, но была перенаправлена ​​на другую, но все в объекте выглядит так, как будто оно подключено к первой ... это даже та же строка).

Java позволяет использовать отражение - и параметры для этого являются строками. Опасность передачи строки, которая может быть изменена путем к другому методу, который отражает. Это очень плохо.

Ключи к хэшу

Хеш-таблица является одной из наиболее часто используемых структур данных. Ключи к структуре данных очень часто являются строками. Наличие неизменяемых строк означает, что (как указано выше) хеш-таблица не должна делать копию хеш-ключа каждый раз. Если бы строки были изменяемыми, а хеш-таблица этого не делала, можно было бы что-то изменить на расстоянии.

Способ работы объекта в Java заключается в том, что все имеет ключ хеша (доступ к которому осуществляется с помощью метода hashCode ()). Наличие неизменяемой строки означает, что hashCode может быть кэширован. Учитывая, как часто строки используются в качестве ключей для хэша, это обеспечивает значительное повышение производительности (вместо того, чтобы каждый раз пересчитывать хэш-код).

Подстроки

Если String будет неизменным, базовый массив символов, который поддерживает структуру данных, также будет неизменным. Это позволяет выполнять определенные оптимизации substringметода (они не обязательно выполняются - это также создает возможность некоторых утечек памяти).

Если вы делаете:

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

Значение bar«миля». Тем не менее, как fooи barможет быть поддержан тот же массив символов, уменьшая экземпляра более массивов символов или копирование его - просто используя различные начальные и конечные точки в пределах строки.

фу | | (0, 6)
    ст
    улыбки
     ^ ^
бар | | (1, 5)

Теперь недостатком этого (утечка памяти) является то, что если бы у вас была строка длиной 1 КБ и была взята подстрока первого и второго символа, она также была бы подкреплена массивом символов 1 КБ. Этот массив останется в памяти, даже если исходная строка, которая имеет значение всего массива символов, была собрана сборщиком мусора.

Это можно увидеть в String из JDK 6b14 (следующий код взят из источника GPL v2 и используется в качестве примера)

   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);
   }

Обратите внимание, как подстрока использует конструктор String уровня пакета, который не включает в себя копирование массива и будет намного быстрее (за счет возможного хранения некоторых больших массивов - хотя и без дублирования больших массивов).

Обратите внимание, что приведенный выше код предназначен для Java 1.6. Способ реализации конструктора подстроки был изменен в Java 1.7, как описано в документе « Изменения во внутреннем представлении String, сделанном в Java 1.7.0_06» - проблема, связанная с утечкой памяти, о которой я упоминал выше. Скорее всего, Java не рассматривался как язык с большим количеством манипуляций со строками, поэтому повышение производительности подстроки было хорошей вещью. Теперь, когда огромные XML-документы хранятся в строках, которые никогда не собираются, это становится проблемой ... и, таким образом, происходит переход к использованию Stringне того же базового массива с подстрокой, чтобы более крупный массив символов можно было собирать быстрее.

Не злоупотребляйте стеком

Один может передать значение строки вокруг вместо ссылки на незыблемую строку , чтобы избежать проблем с изменчивостью. Однако с большими строками передача этого в стек будет ... оскорбительной для системы (помещать целые XML-документы в виде строк в стек и затем снимать их или продолжать передавать их вместе ...).

Возможность дедупликации

Конечно, это не было первоначальной мотивацией того, почему строки должны быть неизменяемыми, но когда кто-то смотрит на рациональное объяснение того, почему неизменяемые строки - хорошая вещь, это, безусловно, нужно учитывать.

Любой, кто немного работал со строками, знает, что они могут высосать память. Это особенно верно, когда вы делаете такие вещи, как извлечение данных из баз данных, которые остаются на некоторое время. Много раз с этими укусами, они снова и снова становятся одной и той же строкой (по одному разу для каждой строки).

Многие крупные Java-приложения в настоящее время имеют узкое место в памяти. Измерения показали, что примерно 25% набора динамических данных кучи Java в этих типах приложений используются объектами String. Кроме того, примерно половина из этих объектов String являются дубликатами, где дубликаты означают значение string1.equals (string2). Наличие дублированных объектов String в куче, по сути, просто трата памяти. ...

В Java 8, обновление 20, JEP 192 (мотивация приведена выше) реализуется для решения этой проблемы. Не вдаваясь в детали того, как работает дедупликация строк, важно, чтобы сами строки были неизменными. Вы не можете дедуплицировать StringBuilders, потому что они могут измениться, и вы не хотите, чтобы кто-то что-то изменял из-под вас. Неизменяемые строки (связанные с этим пулом строк) означают, что вы можете пройти через них, и если вы найдете две одинаковые строки, вы можете указать одну строковую ссылку на другую и позволить сборщику мусора поглотить вновь неиспользованную.

Другие языки

Цель C (которая предшествует Java) имеет NSStringи NSMutableString.

C # и .NET сделали те же самые варианты дизайна строки по умолчанию, являющейся неизменной.

Строки Lua также неизменны.

Питон также.

Исторически, Lisp, Scheme, Smalltalk все интернируют строку и поэтому имеют ее неизменяемость. Более современные динамические языки часто используют строки некоторым образом, который требует, чтобы они были неизменяемыми (это может быть не строка , но она неизменна).

Заключение

Эти конструктивные соображения были сделаны снова и снова на множестве языков. По общему мнению, неизменяемые строки при всей их неловкости лучше альтернатив и приводят к лучшему коду (меньше ошибок) и в целом к ​​более быстрым исполняемым файлам.


источник
3
Java предоставляет изменяемые и неизменяемые строки. В этом ответе подробно описываются некоторые преимущества производительности, которые можно получить для неизменяемых строк, а также некоторые причины, по которым можно выбирать неизменяемые данные; но не обсуждает, почему неизменяемая версия является версией по умолчанию.
Билли ONEAL
3
@BillyONeal: безопасный дефолт и небезопасная альтернатива почти всегда приводят к более безопасным системам, чем противоположный подход.
Йоахим Зауэр
4
@BillyONeal Если неизменяемое не было значением по умолчанию, проблемы параллелизма, безопасности и хэшей были бы более распространенными. Разработчики языка выбрали (частично в ответ на C) язык, на котором установлены значения по умолчанию, чтобы попытаться предотвратить ряд распространенных ошибок, чтобы попытаться повысить эффективность работы программиста (больше не нужно беспокоиться об этих ошибках). В неизменяемых строках меньше ошибок (явных и скрытых), чем в изменяемых.
@Joachim: я не утверждаю иначе.
Билли ONEAL
1
Технически, Common Lisp имеет изменяемые строки для «строковых» операций и символы с неизменяемыми именами для неизменных идентификаторов.
Ватин
21

Причины, которые я могу вспомнить:

  1. Функция пула строк без возможности сделать строку неизменной вообще невозможна, потому что в случае пула строк один строковый объект / литерал, например, «XYZ», будет ссылаться на многие ссылочные переменные, поэтому, если любая из них изменит значение, на другие будут автоматически влиять ,

  2. Строка широко используется в качестве параметра для многих классов Java, например, для открытия сетевого подключения, для открытия подключения к базе данных, открытия файлов. Если String не является неизменяемым, это может привести к серьезной угрозе безопасности.

  3. Неизменность позволяет String кэшировать свой хэш-код.

  4. Делает это потокобезопасным.

простофиля
источник
7

1) Струнный пул

Java-дизайнер знает, что String будет наиболее часто используемым типом данных во всех видах Java-приложений, и поэтому он хотел оптимизировать его с самого начала. Одним из ключевых шагов в этом направлении была идея хранения строковых литералов в строковом пуле. Цель состояла в том, чтобы уменьшить временные объекты String, разделяя их, и для того, чтобы делиться, они должны быть из неизменного класса. Вы не можете поделиться изменяемым объектом с двумя сторонами, которые неизвестны друг другу. Давайте рассмотрим гипотетический пример, где две ссылочные переменные указывают на один и тот же объект String:

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

Теперь, если s1 изменяет объект с «Java» на «C ++», ссылочная переменная также получает значение s2 = «C ++», о котором она даже не знает. Делая String неизменным, это деление строкового литерала стало возможным. Короче говоря, ключевая идея пула String не может быть реализована без окончательного или неизменного String в Java.

2) Безопасность

Java имеет четкую цель с точки зрения обеспечения безопасной среды на каждом уровне обслуживания, и String имеет решающее значение для всей этой системы безопасности. Строка широко используется в качестве параметра для многих классов Java, например, для открытия сетевого подключения вы можете передать хост и порт как String, для чтения файлов в Java вы можете передать путь файлов и каталогов как String, а для открытия подключения к базе данных вы можете передать URL базы данных в виде строки. Если бы String не был неизменным, пользователь мог бы предоставить доступ к определенному файлу в системе, но после аутентификации он может изменить PATH на что-то другое, это может вызвать серьезные проблемы с безопасностью. Аналогично, при подключении к базе данных или любому другому компьютеру в сети, изменяющееся значение String может создавать угрозы безопасности. Изменяемые строки также могут вызвать проблемы с безопасностью в Reflection,

3) Использование строки в механизме загрузки классов

Другая причина сделать String final или Immutable была вызвана тем фактом, что он активно использовался в механизме загрузки классов. Поскольку String не является неизменным, злоумышленник может воспользоваться этим фактом, и запрос на загрузку стандартных классов Java, например, java.io.Reader, может быть изменен на вредоносный класс com.unknown.DataStolenReader. Сохраняя String final и неизменным, мы можем по крайней мере быть уверенными, что JVM загружает правильные классы.

4) Преимущества многопоточности

Поскольку параллелизм и многопоточность были ключевым предложением Java, имело смысл подумать о безопасности потоков в объектах String. Поскольку ожидалось, что String будет широко использоваться, его неизменность означает отсутствие внешней синхронизации, что означает более чистый код, включающий совместное использование String между несколькими потоками. Эта единственная особенность делает намного более сложным, запутанным и подверженным ошибкам параллельное кодирование намного проще. Поскольку String является неизменным, и мы просто разделяем его между потоками, это приводит к более читаемому коду.

5) Оптимизация и производительность

Теперь, когда вы делаете класс Immutable, вы заранее знаете, что этот класс не изменится после его создания. Это гарантирует открытый путь для многих оптимизаций производительности, например, кеширования. Сама строка знает, что менять не собираюсь, поэтому String кеширует свой хеш-код. Он даже вычисляет хэш-код лениво и после его создания просто кеширует его. В простом мире, когда вы в первый раз вызываете метод hashCode () любого объекта String, он вычисляет хеш-код, и все последующие вызовы hashCode () возвращают уже рассчитанное кэшированное значение. Это приводит к хорошему приросту производительности, поскольку String интенсивно используется в картах, основанных на хэше, например, Hashtable и HashMap. Кэширование хеш-кода было невозможно без того, чтобы сделать его неизменным и окончательным, так как это зависит от содержимого самой строки.

Саидеш Килару
источник
5

Виртуальная машина Java выполняет несколько оптимизаций относительно строковых операций, которые не могли бы быть выполнены иначе. Например, если у вас была строка со значением «Mississippi» и вы присвоили «Mississippi» .substring (0, 4) другой строке, насколько вам известно, была сделана копия первых четырех символов, чтобы сделать «Miss» , Чего вы не знаете, так это того, что обе они используют одну и ту же исходную строку «Миссисипи», причем одна является владельцем, а другая - ссылкой на эту строку с позиции 0 до 4. (Ссылка на владельца не позволяет владельцу собирать сборщик мусора, когда владелец выходит за рамки)

Это тривиально для такой маленькой строки, как «Миссисипи», но с более крупными строками и несколькими операциями не нужно копировать строку, что значительно экономит время! Если бы строки были изменяемыми, вы бы не смогли этого сделать, потому что изменение оригинала также повлияло бы на «копии» подстроки.

Кроме того, как упоминает Донал, преимущество было бы значительно ослаблено его недостатком. Представьте, что вы пишете программу, которая зависит от библиотеки, и используете функцию, которая возвращает строку. Как вы можете быть уверены, что это значение останется постоянным? Чтобы ничего подобного не происходило, вам всегда нужно создавать копию.

Что делать, если у вас два потока, разделяющих одну и ту же строку? Вы не хотели бы читать строку, которая в настоящее время переписывается другим потоком, не так ли? Следовательно, String должен был бы быть потокобезопасным, что, будучи его общим классом, сделало бы практически каждую программу Java намного медленнее. В противном случае вам нужно будет сделать копию для каждого потока, для которого требуется эта строка, или вам придется поместить код, использующий эту строку, в блок синхронизации, оба из которых только замедляют вашу программу.

По всем этим причинам это было одно из первых решений, принятых для Java, чтобы отличаться от C ++.

Нил
источник
Теоретически, вы можете выполнять многослойное управление буфером, которое позволяет копировать-при-мутации-если-делиться, но очень сложно эффективно работать в многопоточной среде.
Donal Fellows
@DonalFellows Я просто предположил, что поскольку виртуальная машина Java не написана на Java (очевидно), она управляется внутренне с помощью общих указателей или чего-то подобного.
Нейл
5

Причиной неизменности строки является согласованность с другими примитивными типами в языке. Если у вас есть intзначение 42 и вы добавляете к нему значение 1, вы не изменяете значение 42. Вы получаете новое значение 43, которое совершенно не связано с начальными значениями. Мутирующие примитивы, кроме строки, не имеют концептуального смысла; и поскольку такие программы, которые обрабатывают строки как неизменяемые, часто легче рассуждать и понимать.

Более того, Java действительно предоставляет как изменяемые, так и неизменные строки, как вы видите StringBuilder; на самом деле неизменной является только строка по умолчанию . Если вы хотите передавать ссылки StringBuilderвезде, вы можете это сделать. Java использует отдельные типы ( Stringи StringBuilder) для этих понятий, потому что у нее нет поддержки для выражения изменчивости или ее отсутствия в своей системе типов. В языках, которые поддерживают неизменяемость в своих системах типов (например, C ++ const), часто существует один тип строки, который служит обеим целям.

Да, наличие неизменяемой строки позволяет реализовать некоторые оптимизации, характерные для неизменяемых строк, например интернирование, и позволяет передавать ссылки на строки без синхронизации между потоками. Однако это путает механизм с намеченной целью языка с простой и непротиворечивой системой типов. Я сравниваю это с тем, как все думают о сборе мусора неправильно; сборка мусора - это не «восстановление неиспользуемой памяти»; это «симуляция компьютера с неограниченной памятью» . Обсуждаемая оптимизация производительности - это то, что делается для того, чтобы цель неизменяемых строк работала на реальных машинах; не причина для таких строк быть неизменными в первую очередь.

Билли ОНил
источник
@ Billy-Oneal .. Относительно "Если у вас есть int, содержащий значение 42, и вы добавляете к нему значение 1, вы не меняете 42. Вы получаете новое значение, 43, которое совершенно не связано с начальным ценности." Вы уверены, что?
Шамит Верма
@Shamit: Да, я уверен. Добавление от 1 до 42 результатов в 43. Это не означает, что число 42 означает то же самое, что и число 43.
Billy ONeal
@Shamit: Точно так же вы не можете делать что-то подобное 43 = 6и ожидать, что число 43 будет означать то же самое, что и число 6.
Billy ONeal
int i = 42; я = я + 1; этот код сохранит 42 в памяти и затем изменит значения в том же месте на 43. Таким образом, на самом деле, переменная «i» приобретает новое значение 43.
Shamit Verma
@Shamit: В этом случае вы мутировали i, а не 42. Подумайте string s = "Hello "; s += "World";. Вы мутировали значение переменной s. Но строки "Hello ", "World"и "Hello World"неизменны.
Билли ОНил
4

Неизменность означает, что константы, содержащиеся в принадлежащих вам классах, не могут быть изменены. Классы, которые вам не принадлежат, включают в себя те, которые лежат в основе реализации Java, и строки, которые не следует изменять, включают такие вещи, как маркеры безопасности, адреса служб и т. Д. Вы действительно не должны иметь возможность изменять эти виды вещей (и это применяется вдвойне при работе в режиме песочницы).

Если String не был неизменным, каждый раз, когда вы извлекали его из некоторого контекста, который не хотел, чтобы содержимое строки изменялось у него под ногами, вам приходилось брать копию «на всякий случай». Это становится очень дорогим.

Donal Fellows
источник
4
Точно такой же аргумент применим к любому типу, а не только к String. Но, например, Arrays все же изменчивы. Итак, почему они Stringнеизменны, а Arrayнет. И если неизменность так важна, то почему в Java так сложно создавать неизменные объекты и работать с ними?
Йорг Миттаг,
1
@ JörgWMittag: Я предполагаю, что это в основном вопрос о том, насколько радикальными они хотели быть. Наличие неизменной String было довольно радикальным, еще в Java 1.0 дней. Наличие (в первую очередь или даже исключительно) неизменяемой структуры коллекции также могло бы быть слишком радикальным для широкого использования языка.
Йоахим Зауэр
Создание эффективной платформы неизменяемых коллекций довольно сложно сделать исполнителем, выступая в роли того, кто написал такую ​​вещь (но не на Java). Я также полностью желаю, чтобы у меня были неизменные массивы; это спасло бы меня совсем немного работы.
Donal Fellows
@DonalFellows: pcollections стремится сделать именно это (однако, никогда не использовал это сам).
Йоахим Зауэр
3
@ JörgWMittag: Есть люди (обычно с чисто функциональной точки зрения), которые утверждают, что все типы должны быть неизменными. Аналогичным образом, я думаю, что если вы сложите все проблемы, связанные с работой с изменяемым состоянием в параллельном и параллельном программном обеспечении, вы можете согласиться с тем, что работа с неизменяемыми объектами часто намного проще, чем с изменяемыми.
Стивен Эверс
2

Представьте себе систему, в которой вы принимаете некоторые данные, проверяете их правильность и затем передаете их (например, для хранения в БД).

Предполагается, что данные имеют Stringдлину не менее 5 символов. Ваш метод выглядит примерно так:

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

Теперь мы можем согласиться с тем, что при storeInDatabaseвызове здесь inputбудет соответствовать требованию. Но если бы он Stringбыл изменяемым, то вызывающая сторона могла бы изменить inputобъект (из другого потока) сразу после его проверки и до того, как он был сохранен в базе данных . Это потребует хорошего времени и, вероятно, не всегда будет работать хорошо, но иногда он сможет заставить вас хранить недопустимые значения в базе данных.

Неизменяемые типы данных являются очень простым решением этой (и множества связанных) проблем: всякий раз, когда вы проверяете какое-либо значение, вы можете зависеть от того факта, что проверенное условие все еще остается верным позже.

Йоахим Зауэр
источник
Спасибо за объяснение. Что, если я вызову метод дескриптора как этот; дескриптор (новая строка (вход + "наберлан")). Я думаю, я могу хранить недопустимые значения в БД, как это.
yfklon
1
@blank: хорошо, так как inputв handleметоде уже не является слишком долго (независимо от того , что оригинал input есть), это было бы просто выбросить исключение. Вы создаете новый вход перед вызовом метода. Это не проблема.
Йоахим Зауэр
0

В общем, вы встретите типы значений и ссылочные типы . С типом значения вы не заботитесь об объекте, который его представляет, вы заботитесь о значении. Если я дам вам значение, вы ожидаете, что это значение останется прежним. Вы не хотите, чтобы это внезапно изменилось. Число 5 является значением. Вы не ожидаете, что он внезапно изменится на 6. Строка «Hello» является значением. Вы не ожидаете, что он внезапно изменится на «P *** off».

Со ссылочными типами вы заботитесь об объекте и ожидаете, что он изменится. Например, вы часто будете ожидать изменения массива. Если я дам вам массив, и вы захотите сохранить его таким, какой он есть, вы должны либо довериться мне, чтобы я не изменил его, либо сделать копию.

С помощью строкового класса Java разработчики должны были принять решение: лучше ли строки вести себя как тип значения или они должны вести себя как ссылочный тип? В случае строк Java было принято решение, что они должны быть типами значений, что означает, что, поскольку они являются объектами, они должны быть неизменяемыми объектами.

Противоположное решение могло быть принято, но, по моему мнению, это вызвало бы много головных болей. Как уже было сказано, многие языки приняли одно и то же решение и пришли к такому же выводу. Исключением является C ++, который имеет один класс строк, и строки могут быть постоянными или непостоянными, но в C ++, в отличие от Java, параметры объекта могут передаваться как значения, а не как ссылки.

gnasher729
источник
0

Я действительно удивлен, что никто не указал на это.

Ответ: Это не принесет вам существенной пользы, даже если оно будет изменчивым. Это не принесет вам столько пользы, сколько вызовет дополнительные проблемы. Давайте рассмотрим два наиболее распространенных случая мутации:

Изменение одного символа строки

Поскольку каждый символ в строке Java занимает 2 или 4 байта, спросите себя, получите ли вы что-нибудь, если сможете изменить существующую копию?

В сценарии вы заменяете 2-байтовый символ 4-байтовым (или наоборот), вам нужно сместить оставшуюся часть строки на 2 байта влево или вправо. Что ничем не отличается от копирования всей строки с вычислительной точки зрения.

Это также действительно нерегулярное поведение, которое обычно нежелательно. Представьте, что кто-то тестирует приложение с текстом на английском языке, и когда приложение попадает в другие страны, например в Китай, все начинает работать странным образом.

Присоединение другой строки (или символа) к существующей

Если у вас есть две произвольные строки, они находятся в двух разных местах памяти. Если вы хотите изменить первое, добавив второе, вы не можете просто запросить дополнительную память в конце первой строки, поскольку она, вероятно, уже занята.

Вы должны скопировать объединенную строку в совершенно новое место, которое точно так же, как если бы обе строки были неизменными.

Если вы хотите эффективно добавлять, вы можете использовать его StringBuilder, который резервирует довольно много места в конце строки, просто для возможного добавления в будущем.

Рок Краль
источник
-2
  1. они дорогие, и сохранение их неизменяемыми допускает такие вещи, как подстроки, разделяющие массив байтов основной строки. (увеличение скорости также не требует создания нового байтового массива и копирования)

  2. безопасность - не хотел бы, чтобы ваш пакет или код класса был переименован

    [убрал старый 3, посмотрел на StringBuilder src - он не делит память со строкой (до изменения), я думаю, что это было в 1.3 или 1.4]

  3. кеш хэш-код

  4. для взаимозаменяемых строк используйте SB (компоновщик или буфер при необходимости)

tgkprog
источник
2
1. Конечно, существует штраф за невозможность уничтожить большие части строки, если это произойдет. Стажировка не бесплатная; хотя это действительно улучшает производительность для многих реальных программ. 2. Могут быть строки «string» и «ImmutableString», которые могут удовлетворить это требование. 3. Я не уверен, что понимаю это ...
Billy ONeal
0,3. должен был кешировать хеш-код. Это тоже можно сделать с изменяемой строкой. @ billy-oneal
tgkprog
-4

Строки должны были быть примитивным типом данных в Java. Если бы они были, то строки по умолчанию были бы изменяемыми, а последнее ключевое слово генерировало бы неизменные строки. Изменяемые строки полезны, поэтому существует множество хаков для изменяемых строк в классах stringbuffer, stringbuilder и charsequence.

CWallach
источник
3
Это не отвечает аспекту «почему» того, что сейчас задает вопрос. Кроме того, Java финал не работает таким образом. Изменяемые строки - это не хаки, а реальные конструктивные соображения, основанные на наиболее распространенном использовании строк и оптимизациях, которые можно выполнить для улучшения jvm.
1
Ответ на вопрос «почему» - плохое языковое решение. Три слегка отличающихся способа поддержки изменяемых строк - это хак, который должен обрабатывать компилятор / JVM.
CWallach
3
String и StringBuffer были оригинальными. StringBuilder был добавлен позже, распознавая трудности проектирования с StringBuffer. Изменяемые и неизменяемые строки, являющиеся различными объектами, встречаются во многих языках, так как при проектировании учитывались снова и снова, и было решено, что каждый из них - это разные объекты каждый раз. C # «Строки являются неизменяемыми» и почему .NET String является неизменным? , цель C NSString является неизменной, в то время как NSMutableString является изменяемой. stackoverflow.com/questions/9544182