String, StringBuffer и StringBuilder - PullRequest
204 голосов
/ 04 июня 2010

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

Ответы [ 11 ]

362 голосов
/ 04 июня 2010

Изменчивость:

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

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

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

Ситуация:

  • Если ваша строка не изменится, используйте класс String, поскольку объект String является неизменным.
  • Если ваша строка может измениться (пример: много логики и операций при построении строки) и будет доступна только из одного потока, достаточно использовать StringBuilder.
  • Если ваша строка может измениться и будет доступна из нескольких потоков, используйте StringBuffer, поскольку StringBuffer является синхронным, что обеспечивает безопасность потоков.
46 голосов
/ 04 июня 2010
  • Вы используете String, когда подходит неизменяемая структура; получение новой символьной последовательности из String может привести к неприемлемому снижению производительности, как во время ЦП, так и в памяти (получение подстрок эффективно для ЦП, поскольку данные не копируются, но это означает, что потенциально гораздо больший объем данных может остаться выделенным) .
  • Вы используете StringBuilder, когда вам нужно создать изменяемую последовательность символов, обычно для объединения нескольких последовательностей символов.
  • Вы используете StringBuffer в тех же условиях, что и StringBuilder, но когда изменения в базовой строке должны быть синхронизированы (потому что несколько потоков читают / модифицируют строковый буфер).

См. Пример здесь .

27 голосов
/ 04 июня 2010

Основы:

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

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

Подробности:

Также обратите внимание, что StringBuilder/Buffers не магия, они просто используют массив в качестве вспомогательного объекта и что массив должен быть перераспределен, когда он будет заполнен. Обязательно создайте 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, так как при этом будет выделен только размер строки + 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() при создании фиксированных форматированных выходных данных, которые могут быть оптимизированы компилятором, поскольку они улучшают его.

9 голосов
/ 04 июня 2010

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

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

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

Строка

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 предназначены для строки с именем «строитель»

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

8 голосов
/ 23 ноября 2017

String Family

Строка

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 синхронизирован, поэтому он безопасен для потоков.

* * 1068

Данные 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.

7 голосов
/ 26 сентября 2017
----------------------------------------------------------------------------------
                  String                    StringBuffer         StringBuilder
----------------------------------------------------------------------------------                 
Storage Area | Constant String Pool         Heap                   Heap 
Modifiable   |  No (immutable)              Yes( mutable )         Yes( mutable )
Thread Safe  |      Yes                     Yes                     No
 Performance |     Fast                 Very slow                  Fast
----------------------------------------------------------------------------------
4 голосов
/ 04 июня 2010

Кроме того, StringBuffer является поточно-ориентированным, а StringBuilder - нет.

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

3 голосов
/ 05 сентября 2014

Разница между 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/

3 голосов
/ 06 июня 2010

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

3 голосов
/ 04 июня 2010

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

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

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

...