IT TIP

문자열 연결 연산자 대신 Java에서 StringBuffer를 사용하는 이유

itqueen 2021. 1. 9. 11:12
반응형

문자열 연결 연산자 대신 Java에서 StringBuffer를 사용하는 이유


누군가는 s에 StringBuffer대한 +연산자 를 사용하는 것보다 Java에서 문자열을 연결하는 데 사용 하는 것이 더 효율적이라고 말했습니다 String. 그렇게하면 후드 아래에서 어떤 일이 발생합니까? 어떻게 StringBuffer다른가요?


요즘에는 거의 모든 경우에 StringBuilder (동기화되지 않은 버전입니다. 문자열을 언제 병렬로 빌드합니까?)를 사용하는 것이 좋습니다.하지만 다음과 같은 일이 발생합니다.

두 문자열에 +를 사용하면 다음과 같은 코드가 컴파일됩니다.

String third = first + second;

다음과 같이 :

StringBuilder builder = new StringBuilder( first );
builder.append( second );
third = builder.toString();

따라서 작은 예의 경우 일반적으로 차이가 없습니다. 그러나 복잡한 문자열을 만들 때 이보다 더 많은 것을 처리해야합니다. 예를 들어, 다양한 추가 문을 사용하거나 다음과 같은 루프를 사용할 수 있습니다.

for( String str : strings ) {
  out += str;
}

이 경우, 새로운에서 StringBuilder인스턴스, 새로운 String(새로운 값의 out- StringS는 불변) 각 반복 요구된다. 이것은 매우 낭비입니다. 이것을 단일로 대체하면 단일을 StringBuilder생산할 수 있고 신경 쓰지 않는으로 String힙을 채울 String수 없습니다.


다음과 같은 간단한 연결의 경우 :

String s = "a" + "b" + "c";

사용하는 것은 무의미합니다. jodonnell이 지적했듯이 다음 StringBuffer과 같이 현명하게 번역됩니다.

String s = new StringBuffer().append("a").append("b").append("c").toString();

그러나 다음과 같이 루프에서 문자열을 연결하는 것은 매우 성능이 떨어집니다.

String s = "";
for (int i = 0; i < 10; i++) {
    s = s + Integer.toString(i);
}

이 루프에서 문자열을 사용하면 메모리에 "0", "01", "012"등 10 개의 중간 문자열 개체가 생성됩니다. 동일한 내용을 작성하는 동안 StringBuffer일부 내부 버퍼를 업데이트 StringBuffer하고 필요하지 않은 중간 문자열 객체를 만들지 않습니다.

StringBuffer sb = new StringBuffer();
for (int i = 0; i < 10; i++) {
    sb.append(i);
}

실제로 위의 예 에서는 모든 메소드가 동기화되므로 - StringBuilder대신 (Java 1.5에서 도입 됨)을 사용해야 합니다.StringBufferStringBuffer


하나가 다른 것보다 빠르면 안됩니다. Java 1.4.2 이전에는 그렇지 않았습니다. "+"연산자를 사용하여 두 개 이상의 문자열을 연결할 때 String최종 문자열을 만드는 과정 에서 중간 개체가 생성되기 때문입니다.

그러나 StringBuffer에 대한 JavaDoc이 언급 했듯이 적어도 "+"연산자를 사용하는 Java 1.4.2 이후로 컴파일 하여 많은 문자열을 작성하고 StringBuffer이에 append()대한 문자열을 작성합니다. 그래서 분명히 차이가 없습니다.

그러나 루프 내에서 다른 문자열에 문자열을 추가 할 때는주의하십시오! 예를 들면 :

String myString = "";

for (String s : listOfStrings) {
  // Be careful! You're creating one intermediate String object
  // for every iteration on the list (this is costly!)
  myString += s;
}

그러나 일반적으로 몇 개의 문자열을 "+"로 연결하는 것이 모두 연결하는 것보다 더 깔끔 append()합니다.


내부적으로는 실제로 StringBuffer를 생성하고 추가하여 결과에 대해 toString ()을 호출합니다. 따라서 실제로 더 이상 사용하는 것은 중요하지 않습니다.

그래서

String s = "a" + "b" + "c";

된다

String s = new StringBuffer().append("a").append("b").append("c").toString();

단일 명령문 내에 인라인 추가가 여러 개있는 경우에도 마찬가지입니다. 여러 문을 통해 문자열을 작성하는 경우 메모리를 낭비하고 StringBuffer 또는 StringBuilder가 더 나은 선택입니다.


jdk1.5 이상이 주어지고 연결이 스레드로부터 안전하다고 생각합니다 .StringBuffer http://java4ever.blogspot.com/2007/03/string-vs-stringbuffer-vs-stringbuilder.html 대신 StringBuilder를 사용해야합니다. 속도 향상 : http://www.about280.com/stringtest.html

개인적으로 저는 가독성을 위해 코드를 작성했습니다. 따라서 문자열 연결로 인해 코드가 상당히 느려진다는 사실이 발견되지 않는 한 코드를 더 읽기 쉽게 만드는 방법을 사용하십시오.


어떤 경우에는 컴파일러가 수행하는 최적화로 인해 쓸모가 없지만 일반적인 문제는 다음과 같은 코드입니다.

string myString="";
for(int i=0;i<x;i++)
{
    myString += "x";
}

다음과 같이 작동합니다 (각 단계가 다음 루프 반복 임).

  1. 길이가 1이고 값이 "x"인 문자열 객체를 생성합니다.
  2. 크기 2의 새 문자열 개체를 만들고 이전 문자열 "x"를 여기에 복사 한 다음 위치 2에 "x"를 추가합니다.
  3. 크기 3의 새 문자열 개체를 만들고 이전 문자열 "xx"를 여기에 복사 한 다음 위치 3에 "x"를 추가합니다.
  4. ... 등등

보시다시피 각 반복은 하나 이상의 문자를 복사해야하므로 루프마다 1 + 2 + 3 + 4 + 5 + ... + N 연산을 수행합니다. 이것은 O (n ^ 2) 연산입니다. 그러나 우리가 N 개의 문자 만 필요하다는 것을 미리 알고 있었다면, 우리가 사용하던 문자열에서 N 개의 문자 만 복사하여 단일 할당으로 할 수 있습니다. 단순한 O (n) 연산입니다.

StringBuffer/StringBuilder avoid this because they are mutable, and so do not need to keep copying the same data over and over (so long as there is space to copy into in their internal buffer). They avoid performing an allocation and copy proportional to the number of appends done by over-allocing their buffer by a proportion of its current size, giving amortized O(1) appending.

However its worth noting that often the compiler will be able to optimise code into StringBuilder style (or better - since it can perform constant folding etc.) automatically.


Java turns string1 + string2 into a StringBuffer construct, append(), and toString(). This makes sense.

However, in Java 1.4 and earlier, it would do this for each + operator in the statement separately. This meant that doing a + b + c would result in two StringBuffer constructs with two toString() calls. If you had a long string of concats, it would turn into a real mess. Doing it yourself meant you could control this and do it properly.

Java 5.0 and above seem to do it more sensibly, so it's less of a problem and is certainly less verbose.


AFAIK it depends on version of JVM, in versions prior to 1.5 using "+" or "+=" actually copied the whole string every time.

Beware that using += actually allocates the new copy of string.

As was pointed using + in loops involves copying.

When strings that are conactenated are compile time constants there concatenated at compile time, so

String foo = "a" + "b" + "c";

Has is compiled to:

String foo = "abc"; 

StringBuffer is mutable. It adds the value of the string to the same object without instantiating another object. Doing something like:

myString = myString + "XYZ"

will create a new String object.


To concatenate two strings using '+', a new string needs to be allocated with space for both strings, and then the data copied over from both strings. A StringBuffer is optimized for concatenating, and allocates more space than needed initially. When you concatenate a new string, in most cases, the characters can simply be copied to the end of the existing string buffer.
For concatenating two strings, the '+' operator will probably have less overhead, but as you concatenate more strings, the StringBuffer will come out ahead, using fewer memory allocations, and less copying of data.


The StringBuffer class maintains an array of characters to hold the contents of the strings you concatenate, whereas the + method creates a new string each time its called and appends the two parameters (param1 + param2).

The StringBuffer is faster because 1. it might be able to use its already existing array to concat/store all of the strings. 2. even if they don't fit in the array, its faster to allocate a larger backing array then to generate new String objects for each evocation.


Because Strings are immutable, each call to the + operator creates a new String object and copies the String data over to the new String. Since copying a String takes time linear in the length of the String, a sequence of N calls to the + operator results in O(N2) running time (quadratic).

Conversely, since a StringBuffer is mutable, it does not need to copy the String every time you perform an Append(), so a sequence of N Append() calls takes O(N) time (linear). This only makes a significant difference in runtime if you are appending a large number of Strings together.


As said, the String object is ummutable, meaning once it is created (see below) it cannot be changed.

String x = new String("something"); // or

String x = "something";

So when you attempt to concanate String objects, the value of those objects are taken and put into a new String object.

If you instead use the StringBuffer, which IS mutable, you continually add the values to an internal list of char (primitives), which can be extended or truncated to fit the value needed. No new objects are created, only new char's are created/removed when needed to hold the values.


When you concatenate two strings, you actually create a third String object in Java. Using StringBuffer (or StringBuilder in Java 5/6), is faster because it uses an internal array of chars to store the string, and when you use one of its add(...) methods, it doesn't create a new String object. Instead, StringBuffer/Buider appends the internal array.

In simple concatenations, it's not really an issue whether you concatenate strings using StringBuffer/Builder or the '+' operator, but when doing a lot of string concatenations, you'll see that using a StringBuffer/Builder is way faster.


Further information:

StringBuffer is a thread-safe class


public final class StringBuffer extends AbstractStringBuilder
    implements Serializable, CharSequence
{
// .. skip ..
     public synchronized StringBuffer append(StringBuffer stringbuffer)
    {
        super.append(stringbuffer);
        return this;
    }
// .. skip ..
}

But StringBuilder is not thread-safe, thus it is faster to use StringBuilder if possible


public final class StringBuilder extends AbstractStringBuilder
    implements Serializable, CharSequence
{
// .. skip ..
    public StringBuilder append(String s)
    {
        super.append(s);
        return this;
    }
// .. skip ..
}


Because Strings are imutable in Java, every time you concanate a String, new object is created in memory. SpringBuffer use the same object in memory.


I think the simplest answer is: it's faster.

If you really want to know all the under-the-hood stuff, you could always have a look at the source yourself:

http://www.sun.com/software/opensource/java/getinvolved.jsp

http://download.java.net/jdk6/latest/archive/


The section String Concatenation Operator + of the Java Language Specification gives you some more background information on why the + operator can be so slow.

ReferenceURL : https://stackoverflow.com/questions/65668/why-to-use-stringbuffer-in-java-instead-of-the-string-concatenation-operator

반응형