String은 불변성을 가졌기 때문에 동일한 문자열을 자주 읽어들이는 경우 String을 사용하면 좋다.
그러나 문자열 연산(추가, 수정, 사게)이 자주 일어난다면 계속 새로운 String 인스턴스가 생성되는 문제가 있다.
반면 StringBuffer/StringBuilder는 가변성을 갖기 때문에, 동일한 객체내에서 문자열 변경이 가능하다.
따라서 문자열 연산(추가, 수정, 사게)이 빈번하다면 StringBuffer/StringBuilder를 사용하면 된다.
StringBuffer클래스의 인스턴스를 생성할 때, 적절한 길이의 byte형 배열이 생성되고
이 배열은 문자열을 저장하고 편집하기 위한 공간(buffer) 으로 사용된다.
다음 예제로 살펴보자.
StringBuffer sb = new StringBuffer();
System.out.println(sb.capacity()); // 16
StringBuffer sb2 = new StringBuffer("hi");
System.out.println(sb2.capacity()); // 18
StringBuffer sb3 = new StringBuffer(30);
System.out.println(sb3.capacity()); // 30
String과 다르게 StringBuffer는 내용을 변경할 수 있다.
StringBuffer sb = new StringBuffer("hello");
0 1 2 3 4
h e l l o
StringBuffer를 생성하고, sb에 문자열 "123"을 연결해보자.
sb.append("123");
0 1 2 3 4 5 6 7
h e l l o 1 2 3
hello뒤에 이어서 "123"이 추가된다.
append()는 반환타입이 StringBuffer인데 자기 자신을 반환한다.
StringBuffer sb2 = sb.append("world");
System.out.println(sb); // hello123world
System.out.println(sb2); // hello123world
그 결과 sb와 sb2가 같은 StringBuffer 인스턴스를 가리킨다는 것을 확인할 수 있다.
따라서, 아래와 같이 체인 형태로 사용할 수도 있다.
StringBuffer sb = new StringBuffer("hello");
sb.append("123")
.append("world");
StringBuffer sb = new StringBuffer("abc");
StringBuffer sb2 = new StringBuffer("abc");
System.out.println(sb == sb2); // false
System.out.println(sb.equals(sb2)); // false
StringBuffer 클래스는 equals 메소드를 오버라이딩 하지 않았다.
그래서 equals를 사용해도 등가비교연산자(==)로 비교한것과 같은 결과를 얻는다.
String s = sb.toString();
String s2 = sb2.toString();
System.out.println(s.equals(s2));
toString()은 오버라이딩 되어있기 때문에 toString()을 호출하면 String을 반환한다.
그래서 StringBuffer를 비교하기 위해 toString()을 호출한 뒤 String 끼리 비교해야한다.
StringBuffer 클래스의 메소드에 대해 알아보자. String과 중복되는 내용은 넘어가겠다.
StringBuffer sb = new StringBuffer("hello");
sb.append('w')
.append(10.0f)
.append(123)
.append("world");
System.out.println(sb); // hellow10.0123world
StringBuffer sb = new StringBuffer(100);
System.out.println(sb.capacity()); // 100
StringBuffer sb = new StringBuffer("hello");
System.out.println(sb.length()); // 5
StringBuffer sb = new StringBuffer("0123456");
sb.delete(2, 6);
System.out.println(sb); // 016
StringBuffer sb = new StringBuffer("0123456");
sb.insert(2, "hello");
System.out.println(sb); // 01hello23456
StringBuffer sb = new StringBuffer("0123456");
sb.replace(3, sb.length(), "9999");
System.out.println(sb); // 0129999
StringBuffer sb = new StringBuffer("0123456");
sb.reverse();
System.out.println(sb); // 6543210
StringBuffer는 멀티쓰레드에 안전(thread safe)하도록 동기화되어 있다.
멀티쓰레드가 아닌 싱글쓰레드 환경에서는 StringBuffer의 동기화는 불필요하게 성능을 떨어뜨린다.
그래서 StringBuffer에서 쓰레드의 동기화를 뺀 StringBuilder가 새로 추가되었다.
싱글쓰레드 환경에서는 StringBuilder가 StringBuffer보다 성능이 뛰어나다.
StringBuffer와 StringBuilder는 완전히 똑같은 기능으로 작성되어 있기 때문에
필요하다면 아래와 같이 소스코드를 간단하게 변경할 수 있다.
StringBuffer sb = new StringBuffer();
sb.append("hi");
StringBuilder sb = new StringBuilder();
sb.append("hi");
String
: 문자열 연산이 적고 멀티쓰레드 환경, 자주 조회(참조)하는 경우
StringBuffer
: 문자열 연산이 많고 멀티쓰레드 환경
StringBuilder
: 문자열 연산이 많고 싱글쓰레드 환경이거나 동기화 고려하지 않을 경우