Увы я обычно нахожусь на C#, я думаю что, очищая StringBuilder веса больше, чем instanciating новый.
Второй приблизительно на 25% быстрее в моем мини-сравнительном тесте.
public class ScratchPad {
static String a;
public static void main( String[] args ) throws Exception {
long time = System.currentTimeMillis();
for( int i = 0; i < 10000000; i++ ) {
StringBuilder sb = new StringBuilder();
sb.append( "someString" );
sb.append( "someString2"+i );
sb.append( "someStrin4g"+i );
sb.append( "someStr5ing"+i );
sb.append( "someSt7ring"+i );
a = sb.toString();
}
System.out.println( System.currentTimeMillis()-time );
time = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for( int i = 0; i < 10000000; i++ ) {
sb.delete( 0, sb.length() );
sb.append( "someString" );
sb.append( "someString2"+i );
sb.append( "someStrin4g"+i );
sb.append( "someStr5ing"+i );
sb.append( "someSt7ring"+i );
a = sb.toString();
}
System.out.println( System.currentTimeMillis()-time );
}
}
Результаты:
25265
17969
Примечание, что это с JRE 1.6.0_07.
<час>На основе идей Jon Skeet в редактировании, вот версия 2. Те же результаты все же.
public class ScratchPad {
static String a;
public static void main( String[] args ) throws Exception {
long time = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for( int i = 0; i < 10000000; i++ ) {
sb.delete( 0, sb.length() );
sb.append( "someString" );
sb.append( "someString2" );
sb.append( "someStrin4g" );
sb.append( "someStr5ing" );
sb.append( "someSt7ring" );
a = sb.toString();
}
System.out.println( System.currentTimeMillis()-time );
time = System.currentTimeMillis();
for( int i = 0; i < 10000000; i++ ) {
StringBuilder sb2 = new StringBuilder();
sb2.append( "someString" );
sb2.append( "someString2" );
sb2.append( "someStrin4g" );
sb2.append( "someStr5ing" );
sb2.append( "someSt7ring" );
a = sb2.toString();
}
System.out.println( System.currentTimeMillis()-time );
}
}
Результаты:
5016
7516
Объявите однажды и присвойтесь каждый раз. Это - более прагматическое и допускающее повторное использование понятие, чем оптимизация.
Первое лучше для людей. Если второе немного быстрее на некоторых версиях некоторого JVMs, и что?
, Если производительностью является настолько критический, обходной StringBuilder и запись Ваше собственное. Если Вы - хороший программист и принимаете во внимание, как Ваше приложение использует эту функцию, необходимо быть в состоянии сделать его еще быстрее. Стоящий? Вероятно, нет.
, Почему этот вопрос смотрится как "любимый вопрос"? Поскольку оптимизация производительности является такой забавой, неважно, практично ли это или нет.
Современная JVM действительно умна о материале как это. Я не был бы второе предположение это, и сделайте что-то hacky, который менее удобен в сопровождении/читаем..., если Вы не делаете надлежащие точки отсчета с производственными данными, которые проверяют нетривиальное повышение производительности (и зарегистрируйте его;)
Так как я не думаю, что на это указали все же из-за оптимизации, встроенной в компилятор Java Sun, который автоматически создает StringBuilders (StringBuffers pre-J2SE 5.0), когда это видит Конкатенации строк, первый пример в вопросе эквивалентен:
for (loop condition) {
String s = "some string";
. . .
s += anotherString;
. . .
passToMethod(s);
}
, Который более читаем, IMO, лучший подход. Ваши попытки оптимизировать могут привести к усилениям в некоторой платформе, но потенциально другим потерь.
, Но если Вы действительно сталкиваетесь с проблемами с производительностью, тогда уверенной, оптимизируют далеко. Я запустил бы с явного определения размера буфера StringBuilder хотя на Jon Skeet.
Хорошо, я теперь понимаю то, что продолжается, и это действительно имеет смысл.
у меня создалось впечатление, что toString
просто передал лежание в основе char[]
в конструктора String, который не сделал , делают копию. Копия была бы тогда сделана на следующей операции "записи" (например, delete
). Я полагаю, что это было случай с StringBuffer
в некоторой предыдущей версии. (Это не теперь.), Но нет - toString
просто передачи массив (и индекс и длина) общественности String
конструктор, который делает копию.
Так в "повторном использовании StringBuilder
" случай мы действительно создаем одну копию данных на строку, с помощью того же массива символов в буфере все время. Очевидно, создавая новое StringBuilder
каждый раз создает новый базовый буфер - и затем что буфер скопирован (несколько бессмысленно, в нашем особом случае, но сделан из соображений безопасности) при создании новой строки.
Все это приводит к второй версии, определенно являющейся более эффективным - но в то же время я все еще сказал бы, что это - более ужасный код.
В философии записи тела кодируют всегда лучше для помещения StringBuilder в цикле. Таким образом, это не выходит за пределы кода его намеченное для.
, Во-вторых, самый большой improvment в StringBuilder прибывает из предоставления его начальный размер для предотвращения его становящийся больше, в то время как цикл работает
for (loop condition) {
StringBuilder sb = new StringBuilder(4096);
}
На основе моего опыта с разработкой программного обеспечения в Windows I сказал бы, что убирание StringBuilder во время Вашего цикла имеет лучшую производительность, чем инстанцирование StringBuilder с каждым повторением. Очистка его освобождает ту память, которая будет сразу перезаписана без требуемых дополнительных ассигнований. Я не достаточно знаком со сборщиком "мусора" Java, но я думал бы, что освобождение и никакое перераспределение (если Ваша следующая строка не выращивает StringBuilder) более выгодно, чем инстанцирование.
(Мое мнение противоречит тому, что все остальные предлагают. Хм. Время для сравнительного тестирования его.)
Я не думаю, что это делает с тех пор, чтобы попытаться оптимизировать производительность как этот. Сегодня (2019) оба statments выполняют приблизительно 11 секунд для 100 000 000 циклов на моем Ноутбуке I5:
String a;
StringBuilder sb = new StringBuilder();
long time = 0;
System.gc();
time = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
StringBuilder sb3 = new StringBuilder();
sb3.append("someString");
sb3.append("someString2");
sb3.append("someStrin4g");
sb3.append("someStr5ing");
sb3.append("someSt7ring");
a = sb3.toString();
}
System.out.println(System.currentTimeMillis() - time);
System.gc();
time = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
sb.setLength(0);
sb.delete(0, sb.length());
sb.append("someString");
sb.append("someString2");
sb.append("someStrin4g");
sb.append("someStr5ing");
sb.append("someSt7ring");
a = sb.toString();
}
System.out.println(System.currentTimeMillis() - time);
==> 11 000 мс (объявление в цикле) и 8 236 мс (объявление вне цикла)
, Даже если I'am под управлением программы для адреса dedublication с некоторым миллиардом циклов различие 2 секунд. поскольку 100 миллионов циклов не имеют никакого значения, потому что это программирует, работают в течение многих часов. Также знайте, что вещи отличаются, если Вы только имеете, каждый добавляет оператор:
System.gc();
time = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
StringBuilder sb3 = new StringBuilder();
sb3.append("someString");
a = sb3.toString();
}
System.out.println(System.currentTimeMillis() - time);
System.gc();
time = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
sb.setLength(0);
sb.delete(0, sb.length());
sb.append("someString");
a = sb.toString();
}
System.out.println(System.currentTimeMillis() - time);
==> 3 416 мс (в цикле), 3 555 мс (вне цикла) первый оператор, который создает StringBuilder в цикле, быстрее в этом случае. И при изменении порядка выполнения, это намного больше быстрее:
System.gc();
time = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
sb.setLength(0);
sb.delete(0, sb.length());
sb.append("someString");
a = sb.toString();
}
System.out.println(System.currentTimeMillis() - time);
System.gc();
time = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
StringBuilder sb3 = new StringBuilder();
sb3.append("someString");
a = sb3.toString();
}
System.out.println(System.currentTimeMillis() - time);
==> 3 638 мс (вне цикла), 2 908 мс (в цикле)
С уважением, Ulrich
Еще быстрее:
public class ScratchPad {
private static String a;
public static void main( String[] args ) throws Exception {
long time = System.currentTimeMillis();
StringBuilder sb = new StringBuilder( 128 );
for( int i = 0; i < 10000000; i++ ) {
// Resetting the string is faster than creating a new object.
// Since this is a critical loop, every instruction counts.
//
sb.setLength( 0 );
sb.append( "someString" );
sb.append( "someString2" );
sb.append( "someStrin4g" );
sb.append( "someStr5ing" );
sb.append( "someSt7ring" );
setA( sb.toString() );
}
System.out.println( System.currentTimeMillis()-time );
}
private static void setA( String aString ) {
a = aString;
}
}
В философии написания твердого кода внутренняя работа метода должна быть скрыта от объектов, использующих метод. Таким образом, с точки зрения системы не имеет значения, повторно объявляете ли вы StringBuilder внутри цикла или вне цикла. Поскольку объявление его вне цикла выполняется быстрее и не усложняет чтение кода, используйте объект повторно, а не создавайте его заново.
Даже если код был более сложным, и вы наверняка знали, что этот объект создан. было узким местом, прокомментируйте его.
Три прогона с этим ответом:
$ java ScratchPad
1567
$ java ScratchPad
1569
$ java ScratchPad
1570
Три прогона с другим ответом:
$ java ScratchPad2
1663
2231
$ java ScratchPad2
1656
2233
$ java ScratchPad2
1658
2242
Хотя это не имеет значения, установка начального размера буфера StringBuilder
даст небольшой выигрыш.
Причина, по которой выполняется 'setLength' или 'delete' улучшает производительность - это в основном код, «обучающий» правильный размер буфера, и меньше - выделение памяти. Обычно я рекомендую позволить компилятору выполнять оптимизацию строк . Однако, если производительность критична, я часто заранее рассчитываю ожидаемый размер буфера. Размер StringBuilder по умолчанию составляет 16 символов. Если вы вырастете за пределы этого, его размер придется изменить. Изменение размера - вот где теряется производительность. Вот еще один мини-тест, который это иллюстрирует:
private void clear() throws Exception {
long time = System.currentTimeMillis();
int maxLength = 0;
StringBuilder sb = new StringBuilder();
for( int i = 0; i < 10000000; i++ ) {
// Resetting the string is faster than creating a new object.
// Since this is a critical loop, every instruction counts.
//
sb.setLength( 0 );
sb.append( "someString" );
sb.append( "someString2" ).append( i );
sb.append( "someStrin4g" ).append( i );
sb.append( "someStr5ing" ).append( i );
sb.append( "someSt7ring" ).append( i );
maxLength = Math.max(maxLength, sb.toString().length());
}
System.out.println(maxLength);
System.out.println("Clear buffer: " + (System.currentTimeMillis()-time) );
}
private void preAllocate() throws Exception {
long time = System.currentTimeMillis();
int maxLength = 0;
for( int i = 0; i < 10000000; i++ ) {
StringBuilder sb = new StringBuilder(82);
sb.append( "someString" );
sb.append( "someString2" ).append( i );
sb.append( "someStrin4g" ).append( i );
sb.append( "someStr5ing" ).append( i );
sb.append( "someSt7ring" ).append( i );
maxLength = Math.max(maxLength, sb.toString().length());
}
System.out.println(maxLength);
System.out.println("Pre allocate: " + (System.currentTimeMillis()-time) );
}
public void testBoth() throws Exception {
for(int i = 0; i < 5; i++) {
clear();
preAllocate();
}
}
Результаты показывают, что повторное использование объекта примерно на 10% быстрее, чем создание буфера ожидаемого размера.