String vs StringBuffer vs StringBuilder
String/ StringBuffer/ StringBuilder
Java 에서 문자열을 다루를 대표적인 클래스로 String , StringBuffer, StringBuilder 가 있습니다.
연산이 많지 않을 때는 위에 나열된 어떤 클래스를 사용하더라도 이슈가 발생할 가능성은 거의 없습니다. 그러나 연산횟수가 많아지거나 멀티쓰레드, Race condition 등의 상황이 자주 발생 한다면 각 클래스의 특징을 이해하고 상황에 맞는 적절한 클래스를 사용해 주셔야 합니다!
경쟁상태(Race condition) : 두 개 이상의 cocurrent한 프로세스(혹은 스레드)들이 하나의 자원(리소스)에 접근하기 위해 경쟁하는 상태를 말한다.
String과 StringBuffer, StringBuilder의 차이점
String 객체는 한번 생성되면 할당된 공간이 변하지 않는 불변성(immutable)이 있고, StringBuffer나 StringBuilder의 경우 객체의 공간이 부족해지는 경우 버퍼의 크기를 유연하게 늘려주는 가변성(mutable)이 있습니다.
StringBuffer와 StringBuilder클래스는 둘 다 크기가 유연하게 변하는 가변적인 특성을 가지고 있으며 제공하는 메서드도 같고 사용하는 방법도 동일합니다. 하지만 두 클래스는 동기화 지원의 유무가 다릅니다. StringBuffer는 각 메소드 별로 synchronized keyword가 존재하여 멀티 스레드 상태에서 동기화를 지원하고 StringBuilder는 단일 스레드 환경에서만 사용하도록 설계되어 있습니다. StringBuilder가 StringBuffer보다 속도는 더 빠르지만 현업에서는 언제 멀티스레드 환경에서 돌아가지 알지 못하기에 안정적인 StringBuffer로 통일하여 코딩하는것이 좋습니다.
String vs StringBuffer/StringBuilder
String과 StringBuffer/StringBuilder 클래스의 가장 큰 차이점은 String은 **불변(immutable)**의 속성을 갖는다는 점이다.
String str = "hello"; // String str = new String("hello");
str = str + " world"; // [ hello world ]
위의 예제에서 "hello" 값을 가지고 있던 String 클래스의 참조변수 str이 가리키는 곳에 저장된 "hello"에 "world" 문자열을 더해 "hello world"로 변경한 것으로 착각할 수 있습니다.
하지만 기존에 "hello" 값이 들어가있던 String 클래스의 참조변수 str이 "hello world"라는 값을 가지고 있는 새로운 메모리영역을 가리키게 변경되고 처음 선언했던 "hello"로 값이 할당되어 있던 메모리 영역은 Garbage로 남아있다가 GC(garbage collection)에 의해 사라지게 되는 것 입니다. String 클래스는 불변하기 때문에 문자열을 수정하는 시점에 새로운 String 인스턴스가 생성된 것이다.
즉, String은 같은 값의 문자열에 대해서는 단 하나의 문자열 객체만을 생성하도록 설계 되어있다.
(여기서 의문인게 그럼 GC는 힙 영역에 있는 것을 제거하는데
만약 String str이 지역변수로 선언되면 스택영역에 저장되는데.. 그럼 GC로 제거할 필요도 없는데..
→ 아하 참조타입이라 heap에만 저장이 된다.)
위와 같이 String은 불변성을 가지기 때문에 변하지 않는 문자열을 자주 읽어 들이는 경우 String을 사용해 주시면 좋은 성능을 기대할 수 있습니다. 그러나 문자열 추가,수정,삭제 등의 연산이 빈번하게 발생하는 알고리즘에 String 클래스를 사용하면 힙 메모리(Heap)에 많은 임시 가비지(Garbage)가 생성되어 힙메모리가 부족으로 어플리케이션 성능에 치명적인 영향을 끼치게 됩니다.
이를 해결하기 위해 Java에서는 가변(mutable)성을 가지는 StringBuffer / StringBuilder 클래스를 도입했습니다.
String 과는 반대로 StringBuffer/StringBuilder 는 가변성 가지기 때문에 .append() .delete() 등의 API를 이용하여 동일 객체 내에서 문자열을 변경하는 것이 가능합니다. 따라서 문자열의 추가,수정,삭제가 빈번하게 발생할 경우라면 String 클래스가 아닌 StringBuffer/StringBuilder를 사용해야 한다.
StringBuffer sb= new StringBuffer("hello");
sb.append(" world");
StringBuffer vs StringBuilder
그렇다면 동일한 API를 가지고 있는 StringBuffer, StringBuilder의 차이점은 무엇일까요?
가장 큰 차이점은 동기화의 유무로써 StringBuffer는 동기화 키워드를 지원하여 멀티쓰레드 환경에서 안전하다는 점(thread-safe) 입니다. 참고로 String도 ****불변성을 가지기 때문에 마찬가지로 **멀티쓰레드 환경에서의 안정성(thread-safe)**을 가지고 있습니다.
반대로 StringBuilder는 동기화를 지원하지 않기때문에 멀티쓰레드 환경에서 사용하는 것은 적합하지 않지만 동기화를 고려하지 않는 만큼 단일쓰레드에서의 성능은 StringBuffer 보다 뛰어납니다.
마지막으로 각 클래스별 특징을 정리해 보겠습니다. 컴파일러에서 분석할 때 최적화에 따라 다른 성능이 나올 수도 있지만 일반적인 경우에는 아래와 같은 경우에 맞게 사용하시면 될 것 같네요.
String : 문자열 연산이 적고 멀티쓰레드 환경일 경우
StringBuffer **** : 문자열 연산이 많고 멀티쓰레드 환경일 경우
StringBuilder : 문자열 연산이 많고 단일쓰레드이거나 동기화를 고려하지 않아도 되는 경우
https://t1.daumcdn.net/cfile/tistory/99BE23375E2F133722
String, StringBuffer, StringBuilder 비교
String Pool
String Pool과 String Interning(문자열 객체의 억류)
Java의 Heap에는 String Pool 이라는 특별한 영역에서 String 객체들을 관리합니다. 이 String Pool의 실체는 HashMap입니다. 다음 코드와 같이 문자열 리터럴을 사용하여 String 객체를 생성하면 String Pool에 기존에 같은 값을 가지는 String 객체가 있는지 검사하고 있으면 그 객체의 참조값을 반환하고, 없으면 String Pool에 새로 String 객체를 생성하고 그 참조값을 반환한다.
String a = "aaa";
String b = "aaa";
boolean isSame = a == b; //true
이러한 과정을 intern이라고 하며, String 객체를 위의 코드처럼 리터럴로 생성하는 경우에 내부적으로 intern()이라는 메서드를 실행하여 수행합니다. 따라서 같은 값의 문자열인 a, b 두 객체를 비교하면 참조값을 비교( == )하는 경우에도 true임을 알 수 있습니다.
리터럴을 이용한 String 생성과 new String()을 이용한 String 생성의 차이
문자열은 리터럴을 할당하는것만으로도 생성이 가능하지만 new 연산자와 String() 생성자를 통해서도 생성이 가능하다. 그럼 이 둘의 차이점은 무엇일까요?
String a = "aaa";
String b = "aaa";
String c = new String("aaa");
String d = new String("aaa");
이 둘의 가장 큰 차이점은 객체가 생성되는 영역입니다. 앞서 말했듯이 리터럴을 이용하여 생성하는 경우인 a와 b는 String Pool에 생성되어있는 String 객체를 가리킨다.
반대로 new String("aaa") 으로 실행한 c와 d의 경우 일반 객체들과 마찬가지로 String Pool이 아닌 Heap의 영역에 생성됩니다. String Pool도 엄밀히 따지면 Heap의 내부에 있지만 여기서는 다른 영역으로 생각해주세요.(이부분은 이후에 다룸) 즉 메모리상으로 보면 a, b, c, d 참조변수들은 아래와 같이 각각의 String 객체를 가리키고 있습니다.
위 그림을 바탕으로 테스트를 해보도록 하죠. 결과는 다음과 같습니다.
String a = "aaa";
String b = "aaa";
String c = new String("aaa");
String d = new String("aaa");
System.out.println(a == b); //true
System.out.println(a == c); //false
System.out.println(c == d); //false
System.out.println(a.equals(b)); //true
System.out.println(a.equals(c)); //true
System.out.println(c.equals(d)); //true
출처:
https://ifuwanna.tistory.com/221
[IfUwanna IT]
https://dololak.tistory.com/718
[코끼리를 냉장고에 넣는 방법]