String, StringBuffer 및 StringBuilder


답변:


378

변이성 차이 :

String이다 불변 당신이 그 값을 변경하려고하면, 또 다른 목적은 반면, 작성되는, StringBuffer그리고 StringBuilder있습니다 가변 그들의 값을 변경할 수 있습니다.

스레드 안전성 차이 :

의 차이 StringBuffer와는 StringBuilderStringBuffer스레드 안전합니다. 따라서 응용 프로그램을 단일 스레드에서만 실행해야하는 경우 사용하는 것이 좋습니다 StringBuilder. StringBuilder보다 효율적 StringBuffer입니다.

상황 :

  • 문자열이 변경되지 않으면 String객체는 변경할 수 없으므로 String 클래스를 사용하십시오 .
  • 문자열이 변경 될 수 있고 (예 : 문자열 구성시 많은 논리와 연산) 단일 스레드에서만 액세스 할 수 있다면 a를 사용하는 StringBuilder것으로 충분합니다.
  • 문자열이 변경되어 여러 스레드에서 액세스되는 경우 동기를 사용하므로 스레드 안전성을 갖기 StringBuffer때문에를 사용하십시오 StringBuffer.

16
또한 논리 연산에 문자열을 사용하는 것은 다소 느리며 JVM이 바이트 코드에서 문자열을 StringBuffer로 변환하므로 전혀 권장되지 않습니다. String에서 StringBuffer로 변환 한 다음 다시 String으로 다시 변환하면 많은 오버 헤드가 낭비됩니다.
피터 반 니 에크

2
따라서 Strings값을 변경하면 다른 객체가 만들어집니다. 이전 객체 참조가 무효화되어 가비지 수집 GC되거나 가비지 수집됩니까?
Harsh Vardhan

@PietervanNiekerk 논리 연산의 의미는 무엇입니까?
emlai

논리 연산은 기본 문자열입니다. @ Peter가 말한 것처럼 묻고 싶은 한 가지 사항은 모든 경우에 String 대신 StringBuffer를 사용해야합니까? 특정한 경우가 있습니까?
JavaDragon

@bakkal 수 있습니까 모두의 방법 사용 StringStringBuilder?
roottraveller

47
  • 당신은 사용 String불변의 구조가 적합한 경우; 에서 새로운 문자 시퀀스를 얻는 것은 StringCPU 시간 또는 메모리에서 허용 할 수없는 성능 저하를 가져올 수 있습니다 (데이터가 복사되지 않기 때문에 서브 스트링을 얻는 것이 CPU 효율적이지만, 이는 잠재적으로 훨씬 많은 양의 데이터가 할당 된 상태로 남아있을 수 있음).
  • StringBuilder일반적으로 여러 문자 시퀀스를 연결하기 위해 가변 문자 시퀀스를 만들어야 할 때 사용 합니다.
  • 당신이 사용하는 StringBuffer당신이 사용하는 것과 같은 상황에서 StringBuilder, 그러나 기본 문자열로 변경하는 경우 (여러 개의 스레드가 읽기 때문에 / modifyind 문자열 버퍼)를 동기화해야합니다.

여기 예를 참조 하십시오 .


2
간결하지만 불완전한 것은 StringBuilder / Buffer를 사용해야하는 근본적인 이유를 놓치고 있으며 이는 일반적인 문자열 연결 동작의 재배치 및 배열 복사본을 줄이거 나 제거하는 것입니다.

1
"불변의 문자열을 다룰 때 문자열을 사용합니다"-말이되지 않습니다. String의 인스턴스는 불변이므로 주석은 "불변성으로 인한 메모리 사용량이 중요하지 않을 때 문자열 사용"으로 표시되어야합니다. 허용되는 답변은 기본을 잘 다룹니다.
fooMonster

28

기본 사항 :

String불변의 클래스이므로 변경할 수 없습니다. StringBuilder, 추가 문자를 교체하거나 제거하고 궁극적으로 변환 할 수있는 가변 클래스는 String StringBuffer원래 동기화 버전StringBuilder

StringBuilder객체에 액세스하는 단일 스레드 만있는 모든 경우를 선호해야 합니다.

세부 사항 :

또한 StringBuilder/Buffers마술이 아니라 배열을 백업 개체로 사용하기 때문에 배열이 가득 차면 배열을 다시 할당해야합니다. 호출 StringBuilder/Buffer될 때마다 지속적으로 크기를 조정할 필요가없는 원래 충분히 큰 객체를 작성하십시오 .append().

크기 조정은 매우 저하 될 수 있습니다. 기본적으로 확장해야 할 때마다 기본 배열의 크기를 현재 크기의 2 배로 조정합니다. 이로 인해 많은 양의 RAM이 할당되고 StringBuilder/Buffer클래스가 커지기 시작할 때 사용되지 않을 수 있습니다.

자바 String x = "A" + "B";에서는 StringBuilder배후를 사용 합니다. 따라서 간단한 경우에는 자신을 선언해도 이점이 없습니다. 그러나 String4k 미만의 큰 객체를 빌드하는 경우 선언하는 StringBuilder sb = StringBuilder(4096);것이 연결 또는 16자인 기본 생성자 를 사용하는 것보다 훨씬 효율적 입니다. 당신이 경우 String적은 10K보다가 될 것입니다 후 안전을 위해 10K에 생성자를 초기화합니다. 그러나 10k로 초기화되면 10k보다 큰 1자를 쓰면 다시 할당되고 20k 배열로 복사됩니다. 따라서 높은 초기화는 낮은 것보다 낫습니다.

자동 크기 조정의 경우, 17 번째 문자에서 백업 배열이 재배치되어 32 자로 복사되고, 33 번째 문자에서 다시 발생하고 배열을 64 문자로 다시 할당하고 복사하게됩니다. 이 방법이 많은 재 할당 및 사본으로 어떻게 퇴보되는지 확인할 수 있습니다 StringBuilder/Buffer.

AbstractStringBuilder의 JDK 6 소스 코드에서 가져온 것입니다.

   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못하지만 추측 할 수있는 경우 필요한 것보다 조금 크게 초기화하는 것 입니다. 필요한 것보다 약간 더 많은 메모리를 할당하는 것이 많은 재 할당 및 사본보다 낫습니다.

또한 String + 16 문자의 크기 만 할당 StringBuilder/Buffer하는 Stringas를 사용 하여 a 를 초기화하는 것을주의하십시오 . 대부분의 경우 피하려고하는 퇴화 재 할당 및 복사주기 만 시작합니다. 다음은 Java 6 소스 코드와 동일합니다.

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

우연히 StringBuilder/Buffer생성하지 않았고 호출 된 생성자를 제어 할 수없는 인스턴스가 생길 경우, 재 할당 및 복사 동작의 변형을 피할 수있는 방법이 있습니다. .ensureCapacity()결과 String가 나아지도록 원하는 크기로 전화 하십시오 .

대안 :

참고로, 실제로 무거운 String 건물과 조작을 수행하는 경우 Ropes 라는 훨씬 성능 지향적 인 대안이 있습니다.

또 다른 대안은 StringList서브 클래 싱을 통해 구현 을 생성하고 ArrayList<String>카운터를 추가 .append()하여 목록의 모든 돌연변이 작업에서 문자 수를 추적 한 다음 재정 의하여 필요한 정확한 크기 .toString()를 생성 StringBuilder하고 목록을 반복하여 빌드하는 것입니다. 출력을 StringBuilder통해 인스턴스 변수를 만들고 결과를 '캐시'하고 .toString()무언가가 변경 될 때만 다시 생성해야합니다.

또한 String.format()고정 형식의 출력을 빌드 할 때 잊지 말고 컴파일러가 더 나은 결과를 얻을 수 있도록 최적화하십시오.


1
String x = "A" + "B";실제로 StringBuilder로 컴파일 합니까 ? 그냥 컴파일하지 않는 이유는 String x = "AB";컴파일 타임에 구성 요소를 알 수없는 경우에만 StringBuilder를 사용해야합니다.
Matt Greer

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 vs. 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은 "builder"라는 문자열입니다.

차이점은 없습니다 . 두 문자열에 대해 정확히 동일한 코드가 실행됩니다.


2
이것은 정말 나쁜 예입니다. "Dear"를 사용하여 StringBuilder를 초기화하면 첫 번째 .append ()가 재 할당 및 복사를 유발 함을 의미합니다. "정상"연결을 통해 달성하려는 효율성을 완전히 무시합니다. 더 좋은 예는 최종 문자열의 전체 내용을 담을 초기 크기로 작성하는 것입니다.

1
일반적으로 StringBuilder과제의 오른쪽에 문자열 연결 을 사용하는 것은 좋지 않습니다 . 좋은 구현은 말 그대로 장면 뒤에서 StringBuilder를 사용합니다. 또한 예제 "a" + "b"는 단일 리터럴로 컴파일 "ab"되지만 사용 StringBuilder하면에 대한 두 번의 불필요한 호출이 발생합니다 append().
Mark Peters

@ Mark 나는 사용 "a"+"b"하려고하는 것이 아니라 String 연결 이 무엇인지 명시 적으로 변경했습니다. 당신이 말하지 않는 것은 왜 그렇게하는 것이 좋은 습관이 아니라는 것입니다. 그것이 바로 현대 컴파일러가하는 일입니다. @fuzzy, 나는 최종 문자열의 크기 (aprox)가 무엇인지 알 때 특히 동의합니다.
OscarRyz

1
나는 그것이 나쁜 습관 이라고 생각하지 않지만 , 일반적으로 그렇게하는 권장 사항을 원하지 않을 것입니다. 어색하고 읽기 어렵고 지나치게 장황합니다. 또한 두 개의 리터럴을 분리하여 컴파일 할 수있는 실수를 권장합니다. 프로파일 링에서 나에게 차이가 있다고 말한 경우에만 귀하의 방식으로 처리하겠습니다.
Mark Peters

@ 마크 나는 "템플릿과 같은 코드의 큰 덩어리" 에 대해 더 많이 생각하고 있었지만 실제로 모든 정규 문자열 리터럴에서는 아닙니다. 하지만 그래, 나는 그들이 요즘 같은 일을하기 때문에, 그것은 의미 (10 세 전에 코드 개정의 변화를 거부하는 이유였다) :하지 않습니다, 동의
OscarRyz

8
-------------------------------------------------- --------------------------------
                  String StringBuffer StringBuilder
-------------------------------------------------- --------------------------------                 
저장 영역 | 상수 문자열 풀 힙 힙
수정 가능 | 아니오 (불변) 예 (변동 가능) 예 (변동 가능)
스레드 안전 | 예 예 아니요
 성과 | 매우 빠름
-------------------------------------------------- --------------------------------

왜 문자열 성능이 빠르고 StringBuffer 성능이 매우 느립니까?
gaurav

1
@gaurav 소스 코드를 읽을 수 있습니다 .StringBuffer의 모든 메소드는 synchronised이유 입니다.
Hearen

8

끈 가족

String class문자열을 나타냅니다. Java 프로그램의 모든 문자열 리터럴 "abc"은이 클래스의 인스턴스로 구현됩니다.

String 객체는 불변 그들은 우리가 변경할 수 없습니다 생성되면. ( 문자열은 상수입니다 )

  • 생성자 또는 메소드를 사용하여 문자열을 작성하면 해당 문자열은 힙 메모리 에도 저장 됩니다 SringConstantPool. 그러나 풀에 저장하기 전에 intern()equals 메소드를 사용하여 풀의 동일한 컨텐츠로 오브젝트 가용성을 확인하는 메소드를 호출합니다 . 풀에서 문자열 복사를 사용할 수 있으면 참조를 반환합니다. 그렇지 않으면 String 객체가 풀에 추가되고 참조를 반환합니다.

    • Java 언어는 문자열 연결 연산자 ( +) 및 다른 객체를 문자열로 변환 하는 데 특별한 지원을 제공합니다 . 문자열 연결은 StringBuilder (또는 StringBuffer) 클래스와 해당 append 메서드를 통해 구현됩니다.

    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";

StringBuilderStringBuffer 는 변경 가능한 문자 시퀀스입니다. 즉, 이러한 객체의 값을 변경할 수 있습니다. 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>값 을 정렬 하거나 비교 하려면 Generics의 일부로 이동하십시오 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);
  • 매번 값을 수정하려면 StringBuffer보다 빠른 StringBuilder를 사용하십시오. 여러 스레드가 값을 수정하는 경우 StringBuffer로 이동하십시오.


4

또한 StringBuffer스레드 안전하지만 StringBuilder그렇지 않습니다.

따라서 다른 스레드가 액세스하는 실시간 상황 StringBuilder에서는 결정적이지 않은 결과가 발생할 수 있습니다.


3

Java 5 이상을 사용 StringBuilder하는 경우 대신을 사용해야 합니다 StringBuffer. API 설명서에서 :

릴리스 JDK 5부터이 클래스에는 단일 스레드에서 사용하도록 설계된 동등한 클래스가 추가되었습니다 StringBuilder. StringBuilder는 동일한 동작을 모두 지원하지만 아무런 동기화를 수행하지 않기 때문에 그것이 빠르기 때문 클래스는 일반적으로,이 우선적으로 사용되어야한다.

실제로는 여러 스레드에서 동시에 StringBuffer거의 사용하지 않으므로 동기화 는 거의 항상 불필요한 오버 헤드입니다.


3

개인적으로에 대한 실제 사용은 없다고 생각합니다 StringBuffer. 문자 시퀀스를 조작하여 여러 스레드간에 통신하고 싶은 경우는 언제입니까? 그것은 전혀 유용하게 들리지 않지만 어쩌면 나는 아직 빛을 보지 못했을 것입니다 :)


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

자바에서 문자열 은 변경할 수 없습니다. 변경할 수 없다는 것은 일단 문자열이 생성되면 값을 변경할 수 없음을 의미합니다. StringBuffer 는 변경 가능합니다. StringBuffer 객체가 만들어지면 새 객체를 만드는 대신 내용을 객체의 값에 추가하기 만하면됩니다. StringBuilderStringBuffer 와 비슷하지만 스레드로부터 안전하지 않습니다. StingBuilder의 메소드는 동기화되지 않지만 다른 문자열과 비교하여 Stringbuilder가 가장 빠르게 실행됩니다. String, StringBuilder 및 StringBuffer 를 구현하여 차이점을 배울 수 있습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.