В будние дни я был занят реализацией логики проекта, в субботу у меня было немного времени, поэтому я достал с книжного шкафа толстую английскую версию Thinking In Java и прочитал про сращивание строковых объектов. Сделайте перевод со ссылкой на эту книгу, добавьте свои мысли и напишите эту статью, чтобы записать ее.
Неизменяемый строковый объект
В Java объекты String неизменяемы. В коде вы можете создать несколько псевдонимов для объекта String. Но все эти псевдонимы относятся к одному и тому же.
Например, s1 и s2 являются псевдонимами объекта «droidyue.com», а псевдонимы хранят ссылки на реальные объекты. Итак, s1 = s2
Скопируйте код кода следующим образом:
Строка s1 = "droidyue.com";
Строка s2 = s1;
System.out.println("s1 и s2 имеют одинаковую ссылку =" + (s1 == s2));
Единственный перегруженный оператор в Java
В Java единственный перегруженный оператор связан с конкатенацией строк. +,+=. Кроме того, разработчики Java не допускают перегрузку других операторов.
Анализ сплайсинга
Есть ли на самом деле цена за производительность?
После понимания двух вышеуказанных моментов у вас может возникнуть такая мысль. Поскольку объекты Sting неизменяемы, объединение нескольких (трех и более) строк неизбежно приведет к созданию избыточных промежуточных объектов String.
Скопируйте код кода следующим образом:
String userName = "Энди";
Строка age = "24";
Строковое задание = «Разработчик»;
Строковая информация = имя пользователя + возраст + должность;
Чтобы получить вышеуказанную информацию, имя пользователя и возраст будут объединены для создания временного объекта String t1, содержимое — Andy24, а затем будут объединены t1 и job для создания окончательного информационного объекта, который нам нужен. генерируется промежуточный t1, и создается t1. Далее, если не будет активной переработки, он неизбежно будет занимать определенное количество места. Если это объединение многих (при условии, что их сотен, в основном при вызовах toString объектов) строк, стоимость будет еще выше, а производительность значительно снизится.
Обработка оптимизации компилятора
Действительно ли это связано с затратами на производительность? Нет ли специальной оптимизации обработки для конкатенации строк, которая так часто используется? Ответ: да. Эта оптимизация выполняется, когда компилятор компилирует .java в байт-код.
Если Java-программа хочет работать, ей необходимо пройти два периода: время компиляции и время выполнения. Во время компиляции компилятор Java (Компилятор) преобразует Java-файл в байт-код. Во время выполнения виртуальная машина Java (JVM) запускает байт-код, сгенерированный во время компиляции. За эти два периода Java достигла так называемой компиляции в одном месте и запускалась повсюду.
Давайте поэкспериментируем с тем, какие оптимизации были сделаны во время компиляции, и мы сможем создать фрагмент кода, который может привести к снижению производительности.
Скопируйте код кода следующим образом:
Конкатенация публичного класса {
public static void main(String[] args) {
String userName = "Энди";
Строка age = "24";
Строковое задание = «Разработчик»;
Строковая информация = имя пользователя + возраст + должность;
System.out.println(информация);
}
}
Скомпилируйте Concatenation.java. getConcatenation.class
Скопируйте код кода следующим образом:
javacConcatenation.java
Затем мы используем javap для декомпиляции скомпилированного файла Concatenation.class. javap -c Конкатенация. Если команда javap не найдена, рассмотрите возможность добавления каталога, в котором находится javap, в переменную среды или использования полного пути к javap.
Скопируйте код кода следующим образом:
17:22:04-androidyue~/workspace_adt/strings/src$ javap -c Конкатенация
Скомпилировано из "Concatenation.java"
Конкатенация публичного класса {
общественная конкатенация();
Код:
0: aload_0
1: вызвать специальный #1 // Метод java/lang/Object."<init>":()V
4: возвращение
public static void main(java.lang.String[]);
Код:
0: ldc #2 // Строка Энди
2: асторе_1
3: ldc #3 // Строка 24
5: astore_2
6: ldc #4 // Разработчик строк
8: astore_3
9: новый #5 // класс java/lang/StringBuilder
12: дуп
13: вызывать специальный #6 // Метод java/lang/StringBuilder."<init>":()V
16: загрузка_1
17: вызвать виртуальный #7 // Метод java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: загрузка_2
21: вызвать виртуальный #7 // Метод java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: aload_3
25: вызвать виртуальный #7 // Метод java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: вызвать виртуальный #8 // Метод java/lang/StringBuilder.toString:()Ljava/lang/String;
31: магазин 4
33: getstatic #9 // Поле java/lang/System.out:Ljava/io/PrintStream;
36: загрузить 4
38: ignorevirtual #10 // Метод java/io/PrintStream.println:(Ljava/lang/String;)V
41: возвращение
}
Среди них ldc, astore и т. д. — это инструкции байт-кода Java, аналогичные инструкциям ассемблера. В следующих комментариях для объяснения используется контент, связанный с Java. Мы видим, что существует множество StringBuilder, но мы не вызываем их явно в коде Java. Это оптимизация, выполняемая компилятором Java. Когда компилятор Java обнаруживает сращивание строк, он создает объект StringBuilder и следующее. Сращивание фактически вызывает метод добавления объекта StringBuilder. Таким образом, не возникнет проблем, о которых мы беспокоились выше.
Только оптимизация компилятора?
Поскольку компилятор сделал за нас оптимизацию, достаточно ли просто полагаться на оптимизацию компилятора? Конечно, нет.
Ниже мы рассмотрим фрагмент неоптимизированного кода с меньшей производительностью.
Скопируйте код кода следующим образом:
public void implicitUseStringBuilder (значения String []) {
Строковый результат = "";
for (int i = 0; i <values.length; i++) {
результат += значения[i];
}
System.out.println(результат);
}
Используйте javac для компиляции и javap для просмотра.
Скопируйте код кода следующим образом:
public void implicitUseStringBuilder(java.lang.String[]);
Код:
0: ldc #11 // Строка
2: astore_2
3: iconst_0
4: магазин_3
5: iload_3
6: загрузка_1
7: длина массива
8: if_icmpge 38
11: новый #5 // класс java/lang/StringBuilder
14: дуп
15: вызывать специальный #6 // Метод java/lang/StringBuilder."<init>":()V
18: загрузка_2
19: вызвать виртуальный #7 // Метод java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: загрузка_1
23: iload_3
24: ааааад
25: вызвать виртуальный #7 // Метод java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: вызвать виртуальный #8 // Метод java/lang/StringBuilder.toString:()Ljava/lang/String;
31: astore_2
32: ивк 3, 1
35: перейти к 5
38: getstatic #9 // Поле java/lang/System.out:Ljava/io/PrintStream;
41: загрузка_2
42: ignorevirtual #10 // Метод java/io/PrintStream.println:(Ljava/lang/String;)V
45: возвращение
Среди них 8: if_icmpge 38 и 35: goto 5 образуют цикл. 8: if_icmpge 38 означает, что если целочисленное сравнение стека операндов JVM больше или равно (противоположный результат i <values.length), перейдите к строке 38 (System.out). 35: перейти к 5 означает переход непосредственно к строке 5.
Но здесь очень важно то, что создание объектов StringBuilder происходит между циклами, а это означает, что сколько объектов StringBuilder будет создано за такое же количество циклов, что явно нехорошо. Голый низкоуровневый код.
Небольшая оптимизация может мгновенно улучшить вашу производительность.
Скопируйте код кода следующим образом:
public void явноеUseStringBuider(значения String[]) {
Результат StringBuilder = новый StringBuilder();
for (int i = 0; i <values.length; i++) {
result.append(значения[i]);
}
}
Соответствующая скомпилированная информация
Скопируйте код кода следующим образом:
общественная недействительность явноUseStringBuider(java.lang.String[]);
Код:
0: новый #5 // класс java/lang/StringBuilder
3: дубль
4: вызвать специальный #6 // Метод java/lang/StringBuilder."<init>":()V
7: astore_2
8: iconst_0
9: магазин_3
10: iload_3
11: загрузка_1
12:длина массива
13: if_icmpge 30
16: загрузка_2
17: загрузка_1
18: iload_3
19: ааааад
20: вызвать виртуальный #7 // Метод java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: поп
24: ивк 3, 1
27: перейти к 10
30: возвращение
Как видно из вышеизложенного, 13: if_icmpge 30 и 27: goto 10 образуют цикл, а 0: new #5 находится вне цикла, поэтому StringBuilder не будет создаваться несколько раз.
В общем, нужно стараться избегать явного или неявного создания StringBuilder в теле цикла. Поэтому те, кто понимает, как компилируется код и как он выполняется внутри, могут писать код более высокого уровня.
Если в приведенной выше статье есть ошибки, пожалуйста, раскритиковайте и исправьте их.