2011-01-10 1 views
30

Я читаю «Лучше, быстрее, легче Java» (от Брюса Тейта и Джастина Гетланд), и я знаком с требованиями к удобочитаемости в гибких командах типа, например, что Роберт Мартин обсуждает в своих чистых книгах по кодированию. В команде, в которой я сейчас участвую, мне было сказано явно не использовать оператор +, потому что он создает дополнительные (и ненужные) строковые объекты во время выполнения.StringBuilder/StringBuffer против «+» Оператор

Но это article, Написано в '04, рассказывает о том, как распределение объектов составляет около 10 машинных инструкций. (по существу свободный)

В нем также говорится о том, как GC также помогает сократить расходы в этой среде.

Каковы фактические компромиссы между использованием +, StringBuilder или StringBuffer? (В моем случае это StringBuffer, так как мы ограничены Java 1.4.2.)

StringBufferStringBuffer для меня результаты в уродливом, менее читаемом коде, как показывает несколько примеров в книге Тейта. И StringBuffer синхронизирован по потоку, который, кажется, имеет свои собственные затраты, которые перевешивают «опасность» при использовании оператора +.

Мысли/Мнения?

+2

Похожие queation здесь http://stackoverflow.com/questions/4645020/when-to-use-stringbuilder-in-java – Navi

+0

возможно дубликат [StringBuilder против Объединение строк в ToString () в Java] (http://stackoverflow.com/questions/1532461/stringbuilder-vs-string-concatenation-in-tostring-in-java) –

+0

Возможный дубликат [StringBuilder vs Конкатенация строк в toString() в Java] (http://stackoverflow.com/questions/1532461/stringbuilder-vs-string-concatenation-in-tostring-in-java) –

ответ

41

Использование String конкатенация переведена на StringBuilder операции с помощью компилятора.

Чтобы увидеть, как работает компилятор, я возьму образец класса, скомпилирую его и декомпилирую с помощью jad, чтобы узнать, что такое сгенерированный байт-код.

Оригинальный класс:

public void method1() { 
    System.out.println("The answer is: " + 42); 
} 

public void method2(int value) { 
    System.out.println("The answer is: " + value); 
} 

public void method3(int value) { 
    String a = "The answer is: " + value; 
    System.out.println(a + " what is the question ?"); 
} 

декомпилированный класс:

public void method1() 
{ 
    System.out.println("The answer is: 42"); 
} 

public void method2(int value) 
{ 
    System.out.println((new StringBuilder("The answer is: ")).append(value).toString()); 
} 

public void method3(int value) 
{ 
    String a = (new StringBuilder("The answer is: ")).append(value).toString(); 
    System.out.println((new StringBuilder(String.valueOf(a))).append(" what is the question ?").toString()); 
} 
  • На method1 компилятор выполнил операцию во время компиляции.
  • method2String Конкатенация эквивалентна использованию вручную StringBuilder.
  • method3 Сочетание String определенно плохо, поскольку компилятор создает второй StringBuilder, а не повторно использует предыдущий.

Таким образом, мое простое правило состоит в том, что конкатенации хороши, если вам не нужно снова конкатенировать результат: например, в цикле или когда вам нужно сохранить промежуточный результат.

+3

В спецификации языка Java, ПРЕДЛАГАЕТ, что это то, что сделает компилятор, оставив открытым возможность, что этого не произойдет. 15.18.1.2 Оптимизация конкатенации строк – avgvstvs

+0

Подумайте, как долго эта оптимизация действует в SUN JVM и если она существует в IBM JVM? – avgvstvs

+0

Я думаю, что это, вероятно, здесь, в JVM Sun с версии 1.4, и я не знаю, существует ли она на JVM IBM. Вы можете проверить с помощью декомпилятора, такого как jad, чтобы быть уверенным. – gabuzo

20

Ваша команда должна узнать о reasons for avoiding repeated string concatenation.

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

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

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

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

String x = a + b + c + d; 

, по крайней мере столь же эффективным, как:

StringBuffer buffer = new StringBuffer(); 
buffer.append(a); 
buffer.append(b); 
buffer.append(c); 
buffer.append(d); 
String x = buffer.toString(); 
+0

Компилятор превращает две строки «Тема» + «Предикат» в – avgvstvs

+0

buf.append («Тема»,) .append ("Предикат"). ToString ()? – avgvstvs

+0

@ matt.seil: На самом деле, если есть задействованные константы * *, компилятор Java просто использует «Subject Predicate». Но да, в противном случае он использует StringBuffer/StringBuilder. –

5

Для маленьких сцеплений вы можете просто использовать строку и + для удобочитаемости. Производительность не пострадает. Но если вы делаете много операций конкатенации, отправляйтесь в StringBuffer.

0

Рассмотрим следующий тест производительности, мы построения динамической строки внутри цикла итерации 100000:

public void constructDynamicString() 
    { 
     long startTime = System.currentTimeMillis(); 
     String s = "test"; 
     //StringBuffer s = new StringBuffer("test"); 
     //StringBuilder s = new StringBuilder("test"); 
     for(int i=0; i<100000; i++) 
     { 
      s += "concat"; 
      //s.append("concat"); 
     } 

     long endTime = System.currentTimeMillis(); 

     System.out.println("Concat time ====== " + (endTime - startTime)); 
    } 

Бежим вышеуказанные испытания в 3 раза, каждый подход, в то время, и мы получаем следующий результат :

With String, it tooks: 39068 ms 
With StringBuffer, it tooks: 7ms 
With StringBuilder, it tooks: 4ms 

из приведенных выше результатов, мы замечаем, что с помощью строки объекта для создания динамической строки символов является способом медленнее, чем при использовании StringBuffer и StringBuilder из-за факта непреложности.

Кроме того, StringBuilder быстрее, чем StringBuffer, поскольку он не заботится о синхронизации (хотя это выглядит немного быстрее, скорость будет относительно отличаться, если мы увеличиваем число итераций).

Теперь для того, чтобы решить, какой класс использовать, принимать во внимание следующие факторы:

  1. Если вы создаете статическую строку символов, которые не должны быть изменены в течение выполнения программы, а затем использовать String объект.
  2. Если вы создаете динамическую цепочку символов, которая должна быть разделена между несколькими потоками, рассмотрите возможность использования StringBuffer.
  3. Если оба фактора не существуют (это очень распространенный случай), используйте StringBuilder.

Источник: StringBuilder VS StringBuffer