Использование String в цикле
Наткнулся на информацию, что при конкатенации строк на Java с помощью оператора +, например, в цикле for, каждый раз создается новый объект String, что приводит к потере памяти и увеличению времени работы программы.
Так ли это? И чем заменить сложение строк в цикле? Stringbilder грузит конкретно и точно приводит к повышенному расходу памяти
Ответы (2 шт):
Заменить сложение строк чем-то более быстрым чем StringBuilder практически нечем, так как StringJoiner, String::join, и др. под капотом используют его, впрочем как и сам оператор +, о чём указано в документации:
The implementation of the string concatenation operator is left to the discretion of a Java compiler, as long as the compiler ultimately conforms to The Java™ Language Specification. For example, the javac compiler may implement the operator with
StringBuffer,StringBuilder, orjava.lang.invoke.StringConcatFactorydepending on the JDK version.
Вольный перевод
Реализация оператора конкатенации строк оставляется на усмотрение Java компилятора, поскольку в конечном итоге компилятор соответствует спецификации языка Java JLS. Например, javac компилятор может реализовать этот оператор, используяStringBuffer,StringBuilder, илиjava.lang.invoke.StringConcatFactoryв зависимости от версии JDK.
Также StringBuilder быстрее потокобезопасного StringBuffer за счёт отсутствия синхронизации, о чём также указано в документации, и поэтому именно он рекомендуется в однопоточном коде:
This class provides an API compatible with
StringBuffer, but with no guarantee of synchronization. This class is designed for use as a drop-in replacement forStringBufferin places where the string buffer was being used by a single thread (as is generally the case). Where possible, it is recommended that this class be used in preference toStringBufferas it will be faster under most implementations.
Для увеличения производительности StringBuilder при работе с большими строками, может понадобиться установить его объём capacity при создании экземпляра:
StringBuilder large = new StringBuilder(Integer.MAX_VALUE); // максимальный размер
Эксперимент String += против StringBuilder.
public class ConcatenationString {
public static void main(String... args) {
int n = Integer.parseInt(args[0]);
String s = "";
for (int i = 0; i < n; ++i) {
s += " ";
}
System.out.println(s.length());
}
}
public class ConcatenationStringBuilder {
public static void main(String... args) {
int n = Integer.parseInt(args[0]);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; ++i) {
sb.append(" ");
}
String s = sb.toString();
System.out.println(s.length());
}
}
Сравниваем для тысячи символов:
$ javac -version javac 1.8.0_45 $ javac ConcatenationString.java ConcatenationStringBuilder.java $ time java ConcatenationString 1000 1000 real 0m0.055s user 0m0.052s sys 0m0.004s $ time java ConcatenationStringBuilder 1000 1000 real 0m0.055s user 0m0.044s sys 0m0.008s
Время равное, но это пока. Таблица:
n String StringBuilder 1 0.050с 0.048с 10 0.053с 0.051с 100 0.062с 0.050с 1000 0.054с 0.051с 10000 0.138с 0.062с 100000 4.768с 0.055с 1000000 177.833с 0.067с 10000000 0.188с 100000000 0.978с 1000000000 9.284с
0.05с - это время старта Java, так что маленькие размеры не сравнишь просто так. Рост для String очень быстрый. Рост для StringBuilder медленный, в конце время работы пропорционально конечному размеру строки. На что тратится время? На выделение памяти, копирование пробелов, освобождение памяти и так без конца.
Что можно сказать точно:
Надо мерять время. Без чисел - всё болтовня.
На больших размерах (от 10000 символов) только StringBuilder.
На маленьких размерах - не понятно. Надо мерять время более тонко, тут померяно очень грубо.
Надо понимать что под капотом. Исходный код стандартной библиотеки есть, можно заглянуть.
Надо знать что такое О-большое. Очень сокращает время спора по некоторым вопросам. Этот ответ написан без отсылок к О-большому, потому так длинно.