Строка — один из наиболее часто используемых типов данных в Java. Что касается знаний о строках, автор опубликовал несколько статей и многое представил, например:
Серия изучения исходного кода Java 7 (1) — строка
Как создать строку, используя " " или конструктор?
Наконец-то я понял эту мелочь о String
Три картинки, чтобы полностью понять неизменность строк в Java
Почему Java проектирует строки неизменяемыми
Три изображения, чтобы полностью понять принцип и различие подстроки в JDK 6 и JDK 7.
Конкретные детали реализации Switch в Java для целочисленных, символьных и строковых типов
Эта статья также является дополнением к знаниям, связанным со строками в Java, в основном для ознакомления со знаниями, связанными со сращиванием строк. Эта статья основана на jdk1.8.0_181.
Объединение строк {#toc_0}
Конкатенация строк — это то, что мы часто делаем в коде Java, а именно объединение нескольких строк вместе.
мы все знаем,String — это неизменяемый класс в Java., поэтому после создания экземпляра его нельзя изменить.
После создания экземпляра неизменяемого класса значения его переменных-членов не могут быть изменены. Этот дизайн имеет много преимуществ, таких как кеширование хэш-кода, более удобное использование и повышенная безопасность.
Но поскольку строки неизменяемы, как насчет конкатенации строк?
Неизменяемость строк и конкатенация строк
По сути, все так называемое сращивание строк заключается в том, чтобы регенерировать новую строку. Следующий фрагмент кода конкатенации строк:
String s = "abcd";
s = s.concat("ef");
На самом деле конечная s, которую мы получаем, уже является новой строкой. Как показано ниже
В s сохраняется ссылка на воссозданный объект String.
Итак, как в Java выполнить конкатенацию строк? Существует много способов объединения строк, вот некоторые из наиболее часто используемых.
использовать+объединить строки
В Java самый простой способ конкатенации строк — использовать символы напрямую.+к сращиванию. как:
String wechat = "Hollis";
String introduce = "每日更新Java相关技术文章";
String hollis = wechat + "," + introduce;
Вот особый момент, некоторые люди используют Java+Функция конкатенации строк понимается какперегрузка оператора. Не совсем,Java не поддерживает перегрузку операторов. На самом деле это только один из предоставленных Javaсинтаксический сахар. Подробности позже.
Перегрузка операторов. В компьютерном программировании перегрузка операторов является типом полиморфизма. Перегрузка оператора заключается в переопределении существующего оператора и наделении его другой функцией для адаптации к различным типам данных.
Синтаксический сахар: Синтаксический сахар, также переводимый как «покрытая сахаром грамматика», — это термин, изобретенный британским ученым-компьютерщиком Питером Лендингом для обозначения определенной грамматики, добавленной к компьютерному языку, которая не влияет на функцию языка. удобно для программистов. Синтаксический сахар делает программы более краткими и читабельными.
concat
Помимо использования+В дополнение к объединению строк вы также можете использовать метод concat в классе String для объединения строк. как:
String wechat = "Hollis";
String introduce = "每日更新Java相关技术文章";
String hollis = wechat.concat(",").concat(introduce);
StringBuffer
Что касается строк, в дополнение к определению строки, которую можно использовать для определениястроковая константаизStringВ дополнение к классам также предусмотрены, которые можно использовать для определениястроковая переменнаяизStringBufferКласс, объекты которого можно расширять и изменять.
использоватьStringBufferСтроки могут быть легко объединены. как:
StringBuffer wechat = new StringBuffer("Hollis");
String introduce = "每日更新Java相关技术文章";
StringBuffer hollis = wechat.append(",").append(introduce);
StringBuilder
КромеStringBufferКроме того, есть классStringBuilderтакже может использоваться, его использование иStringBufferпохожий. как:
StringBuilder wechat = new StringBuilder("Hollis");
String introduce = "每日更新Java相关技术文章";
StringBuilder hollis = wechat.append(",").append(introduce);
StringUtils.join
В дополнение к встроенному методу конкатенации строк в JDK вы также можете использовать имена методов конкатенации строк, предоставленные в некоторых библиотеках классов с открытым исходным кодом, таких какapache.commons中который предоставилStringUtilsкласса, из которыхjoinметод объединения строк.
String wechat = "Hollis";
String introduce = "每日更新Java相关技术文章";
System.out.println(StringUtils.join(wechat, ",", introduce));
Вот краткое введение.Основная функция метода соединения, представленного в StringUtils, заключается в объединении массива или коллекции с помощью сплайсера для формирования новой строки, например:
String []list ={"Hollis","每日更新Java相关技术文章"};
String result= StringUtils.join(list,",");
System.out.println(result);
//结果:Hollis,每日更新Java相关技术文章
Кроме того, класс String в Java8 также предоставляет метод статического соединения, который аналогичен по использованию StringUtils.join.
Выше приведены пять наиболее часто используемых способов объединения строк в Java, так какой из них лучше использовать? Почему не рекомендуется использовать его в теле цикла в Руководстве по разработке Java для Alibaba?+Как насчет конкатенации строк?

(Протокол о сращивании строк в Руководстве по разработке Java для Alibaba)
использовать+Принцип реализации соединения строк {#toc_1}
Как упоминалось ранее, использование+Конкатенация строк на самом деле является просто синтаксическим сахаром, предоставляемым Java, поэтому давайте объясним этот синтаксический сахар и посмотрим, как реализуются его внутренние принципы.
Еще этот кусок кода. Давайте декомпилируем сгенерированный им байт-код и посмотрим на результат.
String wechat = "Hollis";
String introduce = "每日更新Java相关技术文章";
String hollis = wechat + "," + introduce;
Декомпилированное содержимое выглядит следующим образом, а инструмент декомпиляции — jad.
String wechat = "Hollis";
String introduce = "\u6BCF\u65E5\u66F4\u65B0Java\u76F8\u5173\u6280\u672F\u6587\u7AE0";//每日更新Java相关技术文章
String hollis = (new StringBuilder()).append(wechat).append(",").append(introduce).toString();
Глядя на код после декомпиляции, мы можем обнаружить, что исходная строковая константа обрабатывается ее методом добавления после преобразования String в StringBuilder в процессе объединения.
То есть в Java+Сращивание струн осуществляется с помощьюStringBuilder.append.
Как реализовано объединение {#toc_2}
Давайте взглянем на исходный код метода concat и посмотрим, как этот метод реализован.
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
Этот код сначала создает массив символов, длина которого является суммой длины существующей строки и строки, подлежащей конкатенации, затем копирует значения двух строк в новый массив символов и использует этот массив символов для создания новый объект String и вернуть его.
Из исходного кода мы также можем видеть, что после метода concat фактически создается новая строка, что также перекликается с проблемой инвариантности строки, о которой мы упоминали ранее.
StringBuffer и StringBuilder
Далее посмотримStringBufferиStringBuilderпринцип реализации.
иStringкласс похожий,StringBuilderКласс также инкапсулирует массив символов, определенный следующим образом:
char[] value;
иStringОтличие в том, что это неfinal, поэтому его можно изменить. Кроме того, сStringРазличные, не обязательно все позиции в массиве символов были использованы, у него есть переменная экземпляра, которая представляет количество символов, которые были использованы в массиве, определяемом следующим образом:
int count;
Исходный код добавления выглядит следующим образом:
public StringBuilder append(String str) {
super.append(str);
return this;
}
Этот класс наследуетAbstractStringBuilderкласс, посмотри на негоappendметод:
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
append будет напрямую копировать символы во внутренний массив символов, если длины массива символов недостаточно, он будет расширен.
StringBufferиStringBuilderАналогично, самая большая разницаStringBufferявляется потокобезопасным, взглянитеStringBufferизappendметод.
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
Этот метод используетsynchronizedОбъявите, что это потокобезопасный метод. иStringBuilderЭто не потокобезопасно.
Как реализован StringUtils.join {#toc_4}
просмотревStringUtils.joinВ исходном коде мы можем обнаружить, что, собственно, он тоже прошелStringBuilderбыть реализованным.
public static String join(final Object[] array, String separator, final int startIndex, final int endIndex) {
if (array == null) {
return null;
}
if (separator == null) {
separator = EMPTY;
}
// endIndex - startIndex > 0: Len = NofStrings *(len(firstString) + len(separator))
// (Assuming that all Strings are roughly equally long)
final int noOfItems = endIndex - startIndex;
if (noOfItems <= 0) {
return EMPTY;
}
final StringBuilder buf = new StringBuilder(noOfItems * 16);
for (int i = startIndex; i < endIndex; i++) {
if (i > startIndex) {
buf.append(separator);
}
if (array[i] != null) {
buf.append(array[i]);
}
}
return buf.toString();
}
Сравнение эффективности {#toc_5}
Поскольку существует так много способов конкатенации строк, какой из них наиболее эффективен? Проведем простое сравнение.
long t1 = System.currentTimeMillis();
//这里是初始字符串定义
for (int i = 0; i < 50000; i++) {
//这里是字符串拼接代码
}
long t2 = System.currentTimeMillis();
System.out.println("cost:" + (t2 - t1));
Мы используем код приведенной выше формы для проверки времени работы каждого из пяти кодов конкатенации строк. Результат выглядит следующим образом:
+ cost:5119
StringBuilder cost:3
StringBuffer cost:4
concat cost:3623
StringUtils.join cost:25726
Как видно из результатов, сравнение времени от короткого к длинному составляет:
StringBuilder<StringBuffer<concat<+<StringUtils.join
StringBufferсуществуетStringBuilderНа основе синхронной обработки это займет относительно больше времени.
StringUtils.join тоже использует StringBuilder, и в нем много других операций, так что это занимает много времени, и это легко понять. На самом деле StringUtils.join лучше справляется с конкатенацией строковых массивов или списков.
Затем возникает проблема, которую мы проанализировали ранее, фактически используя+Также используется принцип реализации сращивания строкStringBuilder, тогда почему результаты такие разные, до более чем 1000 раз?
Давайте декомпилируем следующий код:
long t1 = System.currentTimeMillis();
String str = "hollis";
for (int i = 0; i < 50000; i++) {
String s = String.valueOf(i);
str += s;
}
long t2 = System.currentTimeMillis();
System.out.println("+ cost:" + (t2 - t1));
Декомпилированный код выглядит следующим образом:
long t1 = System.currentTimeMillis();
String str = "hollis";
for(int i = 0; i < 50000; i++)
{
String s = String.valueOf(i);
str = (new StringBuilder()).append(str).append(s).toString();
}
long t2 = System.currentTimeMillis();
System.out.println((new StringBuilder()).append("+ cost:").append(t2 - t1).toString());
Мы видим, что декомпилированный код вforВ цикле каждый разnewвзял одинStringBuilder, а затем положитьStringПревратиться вStringBuilder, а затем продолжитьappend.
Конечно, частое создание новых объектов занимает много времени, что не только отнимает время, но и приводит к пустой трате ресурсов памяти.
Поэтому в Руководстве по разработке Java для Alibaba рекомендуются: тело цикла, метод подключения строк, использованиеStringBuilderизappendметод расширения. Вместо того, чтобы использовать+.
Резюме {#toc_6}
В этой статье рассказывается, что такое сращивание строк.Хотя строки неизменяемы, их все же можно сплайсировать путем создания новых строк.
Существует пять широко используемых методов сращивания строк, которые следует использовать.+,использоватьconcat,использоватьStringBuilder,использоватьStringBufferи использоватьStringUtils.join.
Поскольку новые объекты создаются во время конкатенации строк, необходимо учитывать проблемы с памятью и эффективностью, если конкатенация строк должна выполняться в теле цикла.
Поэтому после сравнения мы обнаружили, что непосредственно с помощьюStringBuilderспособ самый действенный. так какStringBuilderПо своей природе он предназначен для определения изменяемых строк и операций изменения строк.
Однако следует также подчеркнуть, что:
1. Если строка не конкатенирована в теле цикла, используйте ее напрямую+Достаточно.
2. Если конкатенация строк выполняется в параллельных сценариях, используйтеStringBufferзаменитьStringBuilder.