Java에서 문자열 제대로 쓰기

📌 String 제대로 써야 하는 이유

String 클래스는 int, long 등 기본 타입을 제외하면 가장 많이 쓰는 클래스이다. String 클래스는 잘못 사용하면 메모리에 많은 영향을 준다. 만약에 String을 + 연산자로 더하고만 있다면 반성해야 한다. 개발할 때 편할 수 있지만, 메모리를 많이 사용하게 된다.

📌 String, StringBuffer, StringBuilder

문자열을 계속해서 더하는 로직이 있다고 가정해보자. String에서 더하는 것이랑 StringBuilder로 더하는 것은 성능 차이가 매우 많이 난다. 먼저 문자열을 만드는 클래스는 String, StringBuilder, StringBuffer가 가장 많이 사용되는데 이에 대해 알아보자.

StringBuffer, StringBuilder

이 둘의 기능은 동일하다. 차이점은 StringBuffer는 스레드 세이프하게 설계되어 있다. 즉 멀티 스레드 환경에서 문자열을 다룰 때, StringBuffer를 사용하는 것이 안전하다. 하지만 StringBuilder는 단일 스레드에서 안전성만 보장되기 때문에 멀티 스레드 환경에서는 적합하지 않아보인다.

StringBuffer의 생성자

1.
StringBuffer() : 아무것도 없고, 기본으로 16개의 char를 담을 수 있다.
2.
StringBuffer(CharSequence seq) : 해당 CharSequence를 매개변수를 받아 seq 값을 갖는 StringBuffer를 생성한다.
CharSequence는 아래에서 설명하고 있다.
3.
StringBuffer(int capacity) : capacity에 지정한 만큼 담을 수 있는 StringBuffer를 생성한다.
4.
StringBuffer(String str) : str의 값을 갖는 StringBuffer를 생성한다.

CharSequence

인터페이스로 이를 구현한 클래스는 아래와 같다.
CharBuffer
String
StringBuffer
StringBuilder
주로 StringBuffer나 StringBuilder로 생성한 객체를 전달할 때 사용된다. (굳이 toString으로 변경해서 수행할 필요가 없다)
check(new StringBuilder("ABC"); public boolean check(CharSequence seq) { StringBuffer sb = new StringBuffer(seq); return sb.length == 3; }
Java

주요 메서드

append() : 맨 끝에 덧붙인다.
insert() : 지정된 위치 이후에 덧붙인다.
해당 위치가 범위에 없으면 StringIndexOutOfBoundsException이 발생한다.

📌 성능 비교하기

테스트 내용

문자열을 더하는 행위를 100만 번씩 수행한다.

측정 결과

1.
String
응답 시간 : 95,801.41ms (약 95초)
메모리 사용량 : 약 95Gb
2.
StringBuffer
응답 시간 : 247.48ms(append), 14.21ms(toString) (약 0.24초)
메모리 사용량 : 약 28Mb(append), 약 9.5Mb(toString)
3.
StringBuilder
응답 시간 : 174.17ms(apeend), 13.38ms(toString) / 약 0.17초
메모리 사용량 : 약 28Mb(append), 약 9.5Mb(toString)

결과

응답 시간은 String보다 StringBuffer가 약 367배 빠르고, StringBuilder가 약 512배 빠르다.
메모리는 String이 StringBuffer와 StringBuilder보다 3,390배 더 사용한다.

이유

String은 + 연산자로 더하면 기존 객체는 GC 대상이되고, 새로운 객체가 생성되기 때문이다. 이러한 작업이 반복될 수록 메모리를 많이 사용하게 되고, 응답 속도에도 많은 영향을 미칠 수 있다. GC를 하면 시스템의 CPU를 사용하게 되면서 시간도 많이 소요되기 때문이다. 당연한 이야기이지만, 메모리 사용을 최소화하도록 하자.

📌 각각의 용도

String 용도

String을 쓰면 안되는 것은 아니다.
짧은 문자열을 더할 경우는 사용해도 된다.

StringBuffer 용도

현재 스레드에 안전할지 모를 경우나 스레드에 안전할 필요가 있으면 사용한다.
static이 붙은 문자열에 사용하자.
싱글톤인 클래스에 선언된 문자열은 해당 클래스를 이용하자.

StringBuilder 용도

스레드에 안전한지 여부와 관계 없는 프로그램을 개발할 때 사용하라.
지역 변수에 해당 클래스를 사용하자. (지역 변수는 메서드 내에서만 살아있기 때문)

📌 JDK 1.5 이상을 사용한다면?

JDK 1.5 이상에서 아래와 같은 코드를 컴파일 하면 어떻게 될까?
String result = "My " + "number is " + number;
Java
JDK 1.5 이상에서 위 코드는 아래와 같이 컴파일 할 때 변환된다. 이로 인해 실수를 어느 정도 피할 수 있다. 1.5부터는 StringBuilder를 사용하지만, 1.4는 StringBuffer를 사용한다.
String result = new StringBuilder("My ")) .append("number is") .append(number) .toString();
Java

📌 결론

되도록 String 대신 스레드와 관련 있다면 StringBuffer, 그게 아니라면 StringBuilder를 사용하자.
String은 메모리나 응답 속도에 영향을 많이 주므로, 잘 알고 사용하자.
JDK 1.5 이상부터 컴파일 타임에 StringBuilder로 변환하므로, 실수를 막을 수 있다.

📌 참고 자료

TOP