기존의 String
클래스는 문자열을 변경할 수 없다는 단점이 있었다. 그렇기에 문자열을 수정하는 작업을 할때는 변경된 문자열을 새로 메모리에 할당하는 다소 비효율적인 방식을 사용했었다.
그러나, 이번에 배울 StringBuffer
와 StringBuilder
클래스는 문자열을 자유롭게 변경할 수 있는 클래스이다.
StringBuffer 클래스는 문자열을 변경할 수 있는 클래스이다.
StringBuffer
클래스는 문자열을 변경할 수 있는 클래스로, 내부에 문자열 변경을 위한 버퍼(임시 저장장소)를 갖고 있어 문자열을 수정할 수 있으며, StringBuffer
인스턴스를 생성할때 버퍼의 크기를 지정할 수 있다. 또한, 추가적으로 버퍼의 크기를 지정하지 않으면 자동으로 16개의 문자를 저장할 수 있는 버퍼가 생성이 되며, 버퍼가 부족할 경우 버퍼의 크기를 늘리는 작업이 진행되기 때문에 버퍼 크기를 충분하게 지정하는 것이 좋다.
StringBuffer
의 인스턴스 생성은 아래와 같다.
public class Main {
public static void main(String[] args){
StringBuffer sb1 = new StringBuffer("abcd");// string buffer 생성(abcd 저장됨, 버퍼크기 16)
StringBuffer sb2 = new StringBuffer(100);// string buffer 생성(저장된 문자열x, 버퍼크기 100)
}
}
위 코드에서 sb1
에는 값abcd
가 담겨있으며, 버퍼의 크기는 16글자까지 저장될 수 있다. 즉, 만약 문자열을 추가해서 sb1
에 17글자를 입력하게 되면 자동적으로 버퍼의 크기를 늘리게 된다. 이때, 버퍼크기를 늘리는 것은 배열을 확장하는 작업과 동일하므로 효율이 좋지 않다.
sb2
에는 값이 들어있지 않으나, 100글자를 저장할 수 있는 버퍼 크기를 갖는다.
StrnigBuffer
에서 값을 변경하는 원리는 다음과 같다.
StringBuffer
는 기본적으로 버퍼
라는 문자형 배열(char[]
)에 문자열을 저장하는 방식을 사용한다.
아래 그림과 같이 StringBuffer
에 문자가 저장되 있다고 가정하자.
만약 위 배열에서 문자를 수정, 삭제, 추가 하는것은 배열이기에 인덱스에 접근해서 문자를 추가/수정/삭제를 하면 된다. 이렇듯 배열의 원리를 이용해 문자열을 수정하고, 추가하는 것이 Strngbuffer
의 원리이다. 만약 StringBuffer
에서 마지막에 문자열을 추가하고 싶은 경우에는 append()
메소드를 사용하면 된다. 삭제의 경우 delete(int start, int end)
메소드를 사용해 지울 수 있으며, 수정의 경우 replace(int start, int end, Strnig str)
을 이용해 수정할 수 있다. 이때, start와 end는 삭제 및 수정할 배열의 인덱스를 의미한다.
public class Main {
public static void main(String[] args){
StringBuffer sb = new StringBuffer(100);
sb.append("hello");
sb.append(" world\n");
sb.append("hello").append(" world");// 이렇게 해도 동일하게 동작한다
String str= sb.toString();
System.out.println(str);
}
}
output
hello world
hello world
StringBuffer
에서는 문자열을 배열로 비교를 하기 때문에 기존에 사용했던 ==
연산 혹은 equals()
메소드를 사용해 비교를 할 수 없다. 비교를 하기 위해서는 아래와 같이 toString()
메소드를 사용해 문자열로 변환 후 equals()
메소드를 사용해 비교를 해야 한다.
public class Main {
public static void main(String[] args){
StringBuffer sb1 = new StringBuffer(100);
StringBuffer sb2 = new StringBuffer(100);
sb1.append("hello world");
sb2.append("hello world");
String str1 = sb1.toString();
String str2 = sb2.toString();
System.out.println("sb1랑 sb2랑 같은가? "+ str1.equals(str2));
sb1.append("java");
str1 = sb1.toString();
System.out.println("sb1랑 sb2랑 같은가? "+ str1.equals(str2));
}
}
output
sb1랑 sb2랑 같은가? true
sb1랑 sb2랑 같은가? false
Strnig builder
클래스는 StringBuffer
랑 동일하게 문자열을 추가/수정/삭제 할 수 있으며, 메소드 역시 동일하게 사용이 가능하나, 멀티스레드에서 동기화가 되지 않는다
는 차이점이 있다.
멀티스레드에 대해서는 추후에 다루겠지만, StringBuffer
의 경우 멀티스레드 환경에서도 수정된 값들이 정상적으로 적용이 되도록 동기화가 된다. 그러나, StringBuilder
의 경우 멀티스레드 환경에서 동기화가 지원이 되지 않아 멀티스레드 환경에서 적합하지 않다.
물론, StringBuffer
가 더 안전하지만, 싱글스레드 환경에서는 동기화가 필요가 없으므로 동기화 과정이 있는 StringBuffer
보다는 StringBuilder
가 더 성능이 좋다.
지금까지 StringBuffer
와 StringBuilder
에 대해 알아보았다.
이 둘의 경우 둘 다 문자열을 수정할 수 있다는 장점이 있으나, 멀티스레드 환경에서의 동기화에 있어 차이가 존재한다. 그렇다면 문자열을 저장할때 어떤 클래스를 사용해야 효율적인지 알아보도록 하자.
프로그램 파일의 경로 등 문자열이 잘 바뀌지 않는 경우에는 String
클래스를 활용을 하는 것이 가장 효율적이다. StringBuffer
와 StringBuilder
는 결국 toString()
이라는 메소드를 통해 문자열로 바꿔서 출력/비교를 해야 하기 때문에 문자열이 잘 바뀌지 않는 환경에서는 String
클래스를 사용하는 것이 바람직하다.
문자열이 자주 추가/삭제/수정이 되는 경우는 String
클래스는 적합하지 않다. 이런 경우에는 문자열 수정이 쉬운 StringBuffer
클래스 혹은 StringBuilder
클래스를 사용하는 것이 바람직하다.
멀티스레드 환경에서는 동기화 문제로 인해 StringBuffer
를 사용하는 것이 바람직하다. StringBuilder
를 사용하면 동기화가 되지 않아 문자열 수정이 안되는 경우 역시 발생할 수 있기 때문이다.
우리가 지금까지 코딩해왔던 싱글스레드 환경에서는 동기화가 필요하지 않으므로 동기화 연산을 하지 않는 StringBuilder
클래스를 사용하는 것이 더 효율적이다.