개요
코딩테스트 문제를 풀 때 문자열 연산을 진행해야 하는 상황이 생긴다면 효율성을 챙기기 위해서 StringBuilder를 자주 사용하곤 한다. 그런데 문득, String과는 어떤 차이점이 있기에 StringBuilder가 더 효율적으로 문자열 연산을 처리하는지 궁금해졌다.
Java에서 문자열을 다루기 위해 사용되는 자료형 클래스는 String, StringBuilder, StringBuffer가 존재한다. 이 글에서는 이 세 가지 클래스를 비교하고, 위에서 언급한 궁금증에 대한 답을 찾아볼 것이다.
StringBuffer / StringBuilder
StringBuffer 와 StringBuilder 클래스는 문자열을 연산 (추가하거나 변경) 할 때 주로 사용하는 자료형이다.
물론 String 자료형만으로도, + 연산이나 concat() 메소드로 문자열을 붙일 수 있다. 하지만 + 연산자를 이용한 String 인스턴스의 문자열 연산이 많아질수록 공간의 낭비뿐만 아니라 속도 또한 매우 느려지게 된다는 단점이 있다.
String foo = "";
for (int i = 0; i < 100_000; i++) {
foo += "bar"; // 간단하지만 연산 속도가 느리다
}
위의 문제를 해결하기 위해 Java는 문자열 연산을 전용으로 하는 자료형인 StringBuffer를 제공한다. StringBuffer 클래스는 내부적으로 버퍼(buffer)라고 하는 독립적인 공간을 가지게 되어 문자열을 바로 추가할 수 있다. 이로 인해 공간의 낭비도 없으며, 문자열 연산 속도도 매우 빠르다는 특징이 있다.
StringBuffer sb = new StringBuffer();
sb.append("foo");
sb.append(" ");
sb.append("bar");
System.out.println(sb.toString()); // "foo bar"
StringBuilder라는 클래스도 같은 기능을 가지고 있지만, StringBuffer는 멀티쓰레드 환경에 안전하다는 특징이 있다.
기본적으로 버퍼(데이터 공간) 크기의 기본값은 16개의 문자를 저장할 수 있는 크기이며, 생성자를 통해 그 크기를 별도로 설정할 수도 있다. 만약, 문자열 연산 중에 할당된 버퍼의 크기를 넘게 되면 자동으로 버퍼의 크기가 증가된다. 다만, 효율이 떨어질 수 있으므로 버퍼의 크기는 넉넉하게 잡는 것이 좋다.
String vs. StringBuffer / StringBuilder
String
String 클래스는 immutable한 특성이 있다. Immutable이란 변경할 수 없는, 불변의 라는 뜻으로, String의 값은 한 번 생성되면 변경되지 않는다는 뜻이다. 그래서 String으로 문자열 연산을 진행하면 기존의 문자열을 변경할 수 없기 때문에, 매번 연산을 진행할 때마다 내요이 합쳐진 새로운 String 인스턴스를 생성하게 된다.
String str = "foo";
str += "bar";
System.out.println(str); // "foo bar"
위의 코드에서 변수 str가 참조하는 메모리의 "foo" 라는 문자열에 "bar"를 더해 String 객체를 변경한 것을 확인할 수 있다. 이때, 실제로는 "foo bar"라는 문자열이 담긴 새로운 메모리 공간을 생성하고 기존에 "foo"를 갖고 있던 공간은 Garbage Collector의 제거 대상이 된다.
더하기 (+) 연산 외에도, String 객체의 변경에 사용되는 메서드를 사용하더라도 새로운 String 객체를 생성하여 반환한다.
StringBuffer / StringBuilder
StringBuffer와 StringBuilder의 경우 문자열 데이터를 다룬다는 점에서 String 객체와 같지만, 객체의 공간이 부족해지는 경우 버퍼의 크기를 유연하게 늘려주어 가변적(mutable)이라는 차이점이 있다.
두 클래스는 내부 buffer(데이터를 임시로 저장하는 메모리)에 문자열을 저장해두고 그 안에서 추가, 수정, 삭제 작업을 할 수 있도록 설계되어 있다.
String 객체는 한 번 생성되면 불변적인 특징 때문에 값을 변경할 경우, 매 연산 시마다 새로운 문자열을 가진 String 인스턴스가 생성되어 메모리 공간을 차지하게 되지만, StringBuffer / StringBuilder는 가변성을 가지기 때문에 .append(), .delete() 등의 API를 이용하여 동일 객체 내에서 문자열 크기를 변경하는 것이 가능하다.
따라서, 값이 변경될 때마다 새로운 인스턴스를 생성하는 String보다 더 빠르기 때문에 문자열의 수정 작업이 빈번하게 일어나는 상황이라면 StringBuffer / StringBuilder를 사용하는 것이 이상적이다.
StringBuffer vs. StringBuilder
위에서도 설명했지만, StringBuffer와 StringBuilder는 문자열 데이터를 다루는 기능을 제공한다. 두 클래스는 문법이나 배열 구성도 모두 같지만, 동기화(Synchronization)에서의 지원 유무가 다르다.
- StringBuffer 클래스는 쓰레드에서 안전하다 (thread safe).
- StringBuilder 클래스는 쓰레드에서 안전하지 않다 (thread unsafe).
StringBuilder는 동기화를 지원하지 않는 반면, StringBuffer는 동기화를 지원하여 멀티스레드 환경에서도 안전하게 동작할 수 있다.
'Java' 카테고리의 다른 글
[Java] record란 무엇인가 (0) | 2023.12.24 |
---|