String, StringBuffer и StringBuilder

213

Скажите , пожалуйста , реальную ситуацию времени , чтобы сравнить String, StringBufferи StringBuilder?

JavaUser
источник

Ответы:

378

Разница изменчивости:

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

Разница в безопасности потоков:

Разница между StringBufferи в StringBuilderтом, что StringBufferпотокобезопасен. Поэтому, когда приложение необходимо запустить только в одном потоке, его лучше использовать StringBuilder. StringBuilderболее эффективен, чем StringBuffer.

Ситуации:

  • Если ваша строка не будет меняться, используйте класс String, потому что Stringобъект неизменен.
  • Если ваша строка может измениться (пример: множество логики и операций при построении строки) и будет доступна только из одного потока, достаточно использовать a StringBuilder.
  • Если ваша строка может измениться и будет доступна из нескольких потоков, используйте StringBufferпотому что StringBufferявляется синхронным, чтобы обеспечить безопасность потоков.
Bakkal
источник
16
Кроме того, использование String для логических операций является довольно медленным и не рекомендуется вообще, поскольку JVM преобразует String в StringBuffer в байт-коде. Огромные издержки тратятся на преобразование из String в StringBuffer и затем обратно в String.
Питер ван Никерк
2
Поэтому, Stringsкогда мы изменяем значение, создается другой объект. Обнуляется ли старая ссылка на объект, чтобы она могла быть собрана сборщиком мусора GCили даже сборщиком мусора?
Суровый Вардхан
@PietervanNiekerk Что вы подразумеваете под логическими операциями?
Emlai
логические операции, которые я имею в виду, являются простыми строковыми. Теперь я хотел бы спросить одну вещь, о которой говорит @Peter, следует ли нам начинать использовать StringBuffer вместо String во всех случаях или есть какие-то конкретные случаи?
JavaDragon
@bakkal Могу ли я использовать все методы Stringс StringBuilder?
roottraveller
47
  • Вы используете, Stringкогда неизменяемая структура подходит; получение новой символьной последовательности из Stringможет привести к неприемлемому снижению производительности, как во время ЦП, так и в памяти (получение подстрок эффективно при использовании ЦП, поскольку данные не копируются, но это означает, что потенциально гораздо больший объем данных может остаться выделенным).
  • Вы используете, StringBuilderкогда вам нужно создать изменяемую последовательность символов, обычно для объединения нескольких последовательностей символов вместе.
  • Вы используете StringBufferв тех же условиях, что и при использовании StringBuilder, но когда изменения в базовой строке должны быть синхронизированы (потому что несколько потоков читают / модифицируют строковый буфер).

Смотрите пример здесь .

Artefacto
источник
2
сжатый, но неполный, он упускает основную причину использования StringBuilder / Buffer и заключается в том, чтобы уменьшить или исключить перераспределение и копирование массива обычного поведения конкатенации строк.
1
«Вы используете String, когда имеете дело с неизменяемыми строками» - не имеет смысла. Экземпляры String являются неизменяемыми, поэтому, возможно, комментарий должен гласить «Использовать String, когда использование памяти из-за неизменности не имеет значения». Принятый ответ довольно хорошо охватывает его основы.
fooMonster
28

Основы:

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

Вы должны предпочитать StringBuilderво всех случаях, когда у вас есть только один поток доступа к вашему объекту.

Детали:

Также обратите внимание, что StringBuilder/Buffersэто не магия, они просто используют Array в качестве вспомогательного объекта и что Array необходимо перераспределять, когда он заполнен. Будьте уверены, и создавайте ваши StringBuilder/Bufferобъекты достаточно большими, чтобы их не приходилось постоянно менять каждый раз при .append()вызове.

Изменение размеров может стать очень вырожденным. Он в основном изменяет размеры резервного массива в 2 раза по сравнению с его текущим размером каждый раз, когда его необходимо расширить. Это может привести к тому, что большие объемы ОЗУ выделяются и не используются, когда StringBuilder/Bufferклассы начинают расти.

В Java String x = "A" + "B";используется StringBuilderнегласно. Так что для простых случаев нет смысла декларировать свое собственное. Но если вы создаете Stringобъекты большого размера, скажем, менее 4 КБ, тогда объявление StringBuilder sb = StringBuilder(4096);намного эффективнее, чем конкатенация или использование конструктора по умолчанию, который состоит всего из 16 символов. Если у вас Stringбудет меньше 10 КБ, инициализируйте его конструктором до 10 КБ, чтобы быть в безопасности. Но если он инициализируется до 10 КБ, то вы записываете на 1 символ больше 10 КБ, он будет перераспределен и скопирован в массив из 20 КБ. Так что инициализация высокого лучше, чем низкого.

В случае автоматического изменения размера, у 17-го символа резервный массив перераспределяется и копируется в 32 символа, у 33-го это происходит снова, и вы перераспределяете и копируете массив в 64 символа. Вы можете видеть, как это вырождается во многих перераспределениях и копиях, и это то, что вы действительно стараетесь избегать StringBuilder/Bufferв первую очередь.

Это из исходного кода JDK 6 для AbstractStringBuilder

   void expandCapacity(int minimumCapacity) {
    int newCapacity = (value.length + 1) * 2;
        if (newCapacity < 0) {
            newCapacity = Integer.MAX_VALUE;
        } else if (minimumCapacity > newCapacity) {
        newCapacity = minimumCapacity;
    }
        value = Arrays.copyOf(value, newCapacity);
    }

Лучшая практика - инициализировать StringBuilder/Bufferнемного больше, чем вам нужно, если вы не знаете сразу, насколько велик Stringбудет, но вы можете догадаться. Одно выделение чуть больше памяти, чем вам нужно, будет лучше, чем много перераспределений и копий.

Также остерегайтесь инициализации StringBuilder/Bufferс помощью Stringсимвола as, который будет выделять только размер строки String + 16, что в большинстве случаев просто начнет вырожденный цикл перераспределения и копирования, которого вы пытаетесь избежать. Следующее прямо из исходного кода Java 6.

public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
    }

Если вы случайно столкнулись с экземпляром, StringBuilder/Bufferкоторый вы не создали и не можете контролировать вызываемый конструктор, есть способ избежать вырожденного поведения перераспределения и копирования. Позвоните .ensureCapacity()с размером, который вы хотите, чтобы ваш результат Stringсоответствовал.

Альтернативы:

В качестве примечания, если вы делаете действительно тяжелое String построение и манипулирование, есть гораздо более ориентированная на производительность альтернатива, называемая Веревками .

Другой альтернативой является создание StringListреализации путем подкласса ArrayList<String>и добавления счетчиков для отслеживания количества символов в каждой .append()и других операциях мутации в списке, а затем переопределения .toString()для создания StringBuilderнужного вам размера, циклического перемещения по списку и построения. вывод, вы даже можете сделать StringBuilderэто экземпляром переменной и «кэшировать» результаты, .toString()и вам нужно будет только сгенерировать его, когда что-то изменится.

Также не забывайте о String.format()создании фиксированного форматированного вывода, который может быть оптимизирован компилятором, так как он делает его лучше.

user177800
источник
1
String x = "A" + "B";Действительно ли компилируется, чтобы быть StringBuilder? Почему бы просто не скомпилировать String x = "AB";, он должен использовать только StringBuilder, если компоненты не известны во время компиляции.
Мэтт Грир
он может оптимизировать константы String, я не могу вспомнить, когда в последний раз декомпилировал байт-код, но я знаю, что если там есть какие-либо переменные, он наверняка будет использовать реализацию StringBuilder. Вы можете скачать исходный код JDK и узнать сами. «A» + «B» - надуманный пример.
Мне было интересно о String.format (). Я никогда не видел, чтобы его использовали в проектах. Обычно это StringBuilder. Ну, обычно это на самом деле «A» + «B» + «C», потому что люди ленивы;) Я склонен всегда использовать StringBuilder, даже если бы это были только две строки, потому что в будущем, возможно, будет добавлено больше строк , Я никогда не использовал String.format () главным образом потому, что никогда не помнил, какой JDK был представлен - я вижу, что это JDK1.5, я бы использовал его в пользу других опций.
jamiebarrow
9

Вы имеете в виду, для объединения?

Пример из реального мира: вы хотите создать новую строку из множества других .

Например, чтобы отправить сообщение:

строка

String s = "Dear " + user.name + "<br>" + 
" I saw your profile and got interested in you.<br>" +
" I'm  " + user.age + "yrs. old too"

StringBuilder

String s = new StringBuilder().append.("Dear ").append( user.name ).append( "<br>" ) 
          .append(" I saw your profile and got interested in you.<br>") 
          .append(" I'm  " ).append( user.age ).append( "yrs. old too")
          .toString()

Или

String s = new StringBuilder(100).appe..... etc. ...
// The difference is a size of 100 will be allocated upfront as  fuzzy lollipop points out.

StringBuffer (синтаксис такой же, как и у StringBuilder, эффекты отличаются)

Около

StringBuffer против StringBuilder

Первый синхронизирован, а позже нет.

Таким образом, если вы вызываете его несколько раз в одном потоке (что составляет 90% случаев), он StringBuilderбудет работать намного быстрее, потому что не остановится, чтобы увидеть, владеет ли он блокировкой потока.

Таким образом, рекомендуется использовать StringBuilder(если, конечно, у вас есть несколько потоков, обращающихся к нему одновременно, что редко)

Stringконкатенация ( с использованием оператора + ) может быть оптимизирована компилятором для использования StringBuilderпод ней, так что больше не о чем беспокоиться, в древние времена Java это было то, о чем все говорят, что следует избегать любой ценой, потому что каждая конкатенация создал новый объект String. Современные компиляторы больше этого не делают, но все же рекомендуется использовать их на StringBuilderвсякий случай, если вы используете «старый» компилятор.

редактировать

Просто для того, кому интересно, вот что делает компилятор для этого класса:

class StringConcatenation {
    int x;
    String literal = "Value is" + x;
    String builder = new StringBuilder().append("Value is").append(x).toString();
}

javap -c StringConcatenation

Compiled from "StringConcatenation.java"
class StringConcatenation extends java.lang.Object{
int x;

java.lang.String literal;

java.lang.String builder;

StringConcatenation();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   new #2; //class java/lang/StringBuilder
   8:   dup
   9:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   12:  ldc #4; //String Value is
   14:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   17:  aload_0
   18:  getfield    #6; //Field x:I
   21:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   24:  invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   27:  putfield    #9; //Field literal:Ljava/lang/String;
   30:  aload_0
   31:  new #2; //class java/lang/StringBuilder
   34:  dup
   35:  invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   38:  ldc #4; //String Value is
   40:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   43:  aload_0
   44:  getfield    #6; //Field x:I
   47:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   50:  invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   53:  putfield    #10; //Field builder:Ljava/lang/String;
   56:  return

}

Строки с номерами 5 - 27 предназначены для строки с именем «literal».

Строки с номерами 31-53 предназначены для строки с именем «строитель»

Разницы нет, для обеих строк выполняется одинаковый код.

OscarRyz
источник
2
это действительно плохой пример. Инициализация StringBuilder с помощью «Dear» означает, что первый .append () вызовет перераспределение и копирование. Полностью отрицая любую эффективность, которую вы пытаетесь получить по сравнению с «нормальной» конкатенацией. Лучшим примером будет создание его с начальным размером, который будет содержать все содержимое конечной строки.
1
Как правило, НЕ рекомендуется использовать StringBuilderконкатенацию для выполнения строк в правой части назначения. Любая хорошая реализация будет использовать StringBuilder за кулисами, как вы говорите. Кроме того, ваш пример "a" + "b"будет скомпилирован в один литерал, "ab"но если вы StringBuilderего используете, это приведет к двум ненужным вызовам append().
Марк Питерс
@ Марка, которую я не хотел использовать, "a"+"b"но чтобы сказать, что такое конкатенация строк, я изменил ее на явный. То, что вы не говорите, это то, почему не стоит делать это. Это именно то, что делает (современный) компилятор. @ fuzzy, я согласен, особенно если вы знаете, какой будет размер последней строки (aprox).
ОскарРиз
1
Я не думаю, что это особенно плохая практика, но я, конечно, не хотел бы, чтобы какая-либо рекомендация делала это вообще. Это неуклюже и трудно читать, и слишком многословно. Кроме того, он поощряет ошибки, подобные вашей, когда вы разбиваете два литерала, которые в противном случае могли бы быть скомпилированы как один. Только если профилирование скажет мне, что это имеет значение, я сделаю это по-вашему.
Марк Питерс
@ Марк понял. Я больше думал о «большой части кода, как шаблон», а не о каждом обычном строковом литерале. Но да, я согласен, поскольку в настоящее время они делают то же самое, это не имеет смысла (10 лет назад была причина отклонить изменение в пересмотре кода) :)
OscarRyz
8
-------------------------------------------------- --------------------------------
                  String StringBuffer StringBuilder
-------------------------------------------------- --------------------------------                 
Складское помещение | Постоянная куча кучи пула
Модифицируемый | Нет (неизменяемый) Да (изменяемый) Да (изменяемый)
Поток Безопасный | Да да нет
 Производительность | Быстро очень медленно быстро
-------------------------------------------------- --------------------------------
Абхиджит Мэйти
источник
Почему производительность String высокая, а производительность StringBuffer очень низкая?
Гаурав
1
@gaurav вы можете прочитать его исходный код, все методы в StringBuffer есть synchronisedи вот почему .
Слушай
8

Струнная семья

строка

String classПредставляет собой строку символов. Все строковые литералы в Java-программе, например "abc", реализованы как экземпляры этого класса.

Строковые объекты неизменны, как только они созданы, мы не можем их изменить. ( Строки являются постоянными )

  • Если строка создается с помощью конструктора или метода , то эти строки будут храниться в динамической памяти , а также SringConstantPool. Но перед сохранением в пуле он вызывает intern()метод проверки доступности объекта с тем же содержимым в пуле, используя метод equals. Если в пуле имеется String-copy, возвращается ссылка. В противном случае объект String добавляется в пул и возвращает ссылку.

    • Язык Java обеспечивает специальную поддержку для оператора конкатенации строк ( +) и для преобразования других объектов в строки. Конкатенация строк реализуется через класс StringBuilder (или StringBuffer) и его метод добавления.

    String heapSCP = new String("Yash");
    heapSCP.concat(".");
    heapSCP = heapSCP + "M";
    heapSCP = heapSCP + 777;
    
    // For Example: String Source Code 
    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);
    }
  • Строковые литералы хранятся в StringConstantPool.

    String onlyPool = "Yash";

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

  • Данные StringBuffer и StringBuilder могут быть созданы только с использованием оператора new. Таким образом, они сохраняются в памяти кучи.

  • Экземпляры StringBuilder не безопасны для использования несколькими потоками. Если такая синхронизация требуется, то рекомендуется использовать StringBuffer.

    StringBuffer threadSafe = new StringBuffer("Yash");
    threadSafe.append(".M");
    threadSafe.toString();
    
    StringBuilder nonSync = new StringBuilder("Yash");
    nonSync.append(".M");
    nonSync.toString();
  • StringBuffer и StringBuilder оказывают специальные методы , такие как., replace(int start, int end, String str)И reverse().

    ПРИМЕЧАНИЕ : StringBuffer и SringBuilder являются изменяемыми, поскольку они обеспечивают реализацию Appendable Interface.


Когда использовать какой.

  • Если вы не собираетесь менять значение каждый раз, то лучше использовать String Class. Как часть Обобщений, если вы хотите Сортировать Comparable<T>или сравнить значения, тогда переходите к String Class.

    //ClassCastException: java.lang.StringBuffer cannot be cast to java.lang.Comparable
    Set<StringBuffer> set = new TreeSet<StringBuffer>();
    set.add( threadSafe );
    System.out.println("Set : "+ set);
  • Если вы собираетесь каждый раз изменять значение, используйте StringBuilder, который работает быстрее, чем StringBuffer. Если несколько потоков изменяют значение, переходите к StringBuffer.

Яши
источник
4

Кроме того, StringBufferпотокобезопасен, что StringBuilderне так.

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

Ларс Андрен
источник
3

Обратите внимание, что если вы используете Java 5 или новее, вы должны использовать StringBuilderвместо StringBuffer. Из документации API:

Начиная с выпуска JDK 5 этот класс был дополнен эквивалентным классом, предназначенным для использования одним потоком StringBuilder. Этот StringBuilderкласс, как правило, следует использовать предпочтительнее этого, поскольку он поддерживает все те же операции, но он быстрее, поскольку не выполняет синхронизацию.

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

Jesper
источник
3

Лично я не думаю, что есть реальная польза для этого StringBuffer. Когда я захочу общаться между несколькими потоками, манипулируя последовательностью символов? Звучит совсем не полезно, но, может быть, мне еще предстоит увидеть свет :)

fredoverflow
источник
3

Разница между String и двумя другими классами заключается в том, что String является неизменным, а два других являются изменяемыми классами.

Но почему у нас есть два класса для одной цели?

Причина в том, что StringBufferявляется потокобезопасным и StringBuilderне является. StringBuilderЭто новый класс, StringBuffer Apiкоторый был введен JDK5и всегда рекомендуется, если вы работаете в однопоточной среде, так какFaster

Для получения полной информации вы можете прочитать http://www.codingeek.com/java/stringbuilder-and-stringbuffer-a-way-to-create-mutable-strings-in-java/

Хитеш Гарг
источник
2

В Java String является неизменным. Будучи неизменными, мы имеем в виду, что после создания строки мы не можем изменить ее значение. StringBuffer является изменяемым. После создания объекта StringBuffer мы просто добавляем содержимое к значению объекта, а не создаем новый объект. StringBuilder похож на StringBuffer, но он не является потокобезопасным. Методы StingBuilder не синхронизированы, но по сравнению с другими строками Stringbuilder работает быстрее всего. Вы можете узнать разницу между String, StringBuilder и StringBuffer , реализовав их.

Раджат Гхай
источник