[Java] String, StringBuffer, StringBuilder

오늘은 자바에서 문자열을 다루는 클래스인 String, StringBuffer, StringBuilder에 대해 함께 알아보도록 하겠습니다.
1. String이란?
Java에서 문자열을 다루는 클래스로, 불변(immutable)이라는 특징을 가져 한 번 생성된 문자열은 변경할 수 없습니다.
1-1. String 특징
문자열을 저장할 때 문자 배열 char[]을 사용합니다. 따라서 인덱스를 사용해, 각 문자열의 문자에 접근이 가능합니다.
1-2. String 예시
String 클래스에서 주로 사용되는 변수나 메서드를 아래 예시에 작성해뒀습니다.
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
// String 객체 생성 방법
String str1 = "Hello"; // 리터럴 방식
String str2 = new String("World"); // 생성자 방식
// 문자열 비교
boolean isEqual = str1.equals(str2); // equals 메서드 사용
System.out.println("str1과 str2는 같은 문자열인가? " + isEqual);
// 문자열 연결
String str3 = str1 + " " + str2; // + 연산자 사용
System.out.println("연결된 문자열: " + str3);
// 문자열 길이
int length = str3.length();
System.out.println("str3의 길이: " + length);
// 문자열 추출
String subStr = str3.substring(6);
System.out.println("추출된 문자열: " + subStr);
// 문자열 치환
String replacedStr = str3.replace("World", "Java");
System.out.println("치환된 문자열: " + replacedStr);
// 문자열 분리
String[] splitStr = str3.split(" ");
System.out.println("분리된 문자열: " + Arrays.toString(splitStr));
// 문자 추출
char ch = str3.charAt(6);
System.out.println("추출된 문자 : " + ch);
// 문자열 찾기
int index = str3.indexOf('W');
System.out.println("문자 W의 인덱스 번호 : " + index);
// 문자열 포함 여부
boolean contains = str3.contains("llo");
System.out.println("str3에 \"llo\" 포함되었는가? " + contains);
// 앞뒤 공백 제거
String str4 = " Bye Java ";
String trimStr = str4.trim();
System.out.println("앞뒤 공백 제거 : " + trimStr);
// 모든 타입 -> String 변환
int num = 100;
String strNum = String.valueOf(num);
System.out.println("num -> str : " + strNum);
}
}
<출력>
str1과 str2는 같은 문자열인가? false
연결된 문자열: Hello World
str3의 길이: 11
추출된 문자열: World
치환된 문자열: Hello Java
분리된 문자열: [Hello, World]
추출된 문자 : W
문자 W의 인덱스 번호 : 6
str3에 "llo" 포함되었는가? true
앞뒤 공백 제거 : Bye Java
num -> str : 100
문자열 비교 |
문자열 연결 |
문자열 길이 |
문자열 추출 |
문자 추출 |
문자열 분리 |
문자열 치환 |
문자열 포함 여부 |
앞뒤 공백 제거 |
String 으로 변환 |
equals | +, concat | length | substring | charAt | split | replace | contains | trim | valueOf |
1-2-1. valueOf()
valueOf 메서드는 기본 자료형이나 객체를 문자열로 변환합니다.
valueOf 메서드는 여러 자료형에 오버로딩 되어 있어, 해당 자료형의 값을 받아 해당 자료형을 문자열로 변환해 반환합니다.
int num = 10;
String str1 = String.valueOf(num); // "10"
double d = 3.14;
String str2 = String.valueOf(d); // "3.14"
char[] char = {'a', 'b', 'c'};
String str3 = String.valueOf(char); // "abc"
boolean bool = true;
String str4 = String.valueOf(bool); // "true"
이 챕터에서 주목해야 할 것은 String.valueOf(char)와 String.valueOf(char[]) 입니다.
char 배열을 생성하고,
1. 배열 전체를 문자열로 변환하는 것과 2. 각 요소를 문자열로 변환한 뒤 배열에 저장하는 예시도 함께 보도록 하겠습니다.
public class Main {
public static void main(String[] args) {
char[] cArr = {'J', 'a', 'v', 'a'};
// 1. 문자열로 변환
String str = String.valueOf(cArr);
System.out.println(str);
// 2. 각 요소를 문자열로 변환한 뒤 배열에 저장
String[] strArr = new String[cArr.length];
for (int i = 0; i < cArr.length; i++) {
strArr[i] = String.valueOf(cArr[i]);
}
System.out.println(Arrays.toString(strArr));
}
}
<출력 결과>
Java
[J, a, v, a]
1-2-2. reverse()
String 클래스에는 reverse() 메서드가 존재하지 않습니다.
그러나 아래 StringBuffer나 StringBuilder 클래스의 reverse() 메서드를 활용하여 문자열을 역순으로 출력할 수 있습니다.
아래의 예시를 통해 자세히 알아보도록 하겠습니다.
String str = "hello";
String reversedStr = new StringBuilder(str).reverse().toString();
System.out.println(reversedStr); // "olleh" 출력
위의 예시에서는reversedStr이라는 String 객체를 선언하고,
StringBuilder 객체 생성과 동시에 reverse() 메서드로 문자열을 뒤집어
toString()을 통해 다시 String 객체로 변환하여 반환합니다.
이와 같이 StringBuffer/StringBuilder 클래스의 reverse()를 활용하여 String도 역순 출력할 수 있다는 것을 기억하면 좋을 것 같습니다.
2. StringBuffer란?
가변적인 문자열을 처리하기 위한 클래스로, 문자열을 수정할 수 있습니다.
2-1. StringBuffer 특징
StringBuffer는 동기화를 보장하기 때문에 멀티스레드 환경에서 안전하게 사용할 수 있습니다.
더불어 String은 문자열을 수정하기 위해 새로운 객체를 생성해야 했지만,
StringBuffer는 기존 객체 내에서 문자열을 수정할 수 있습니다. (String 클래스보다 메모리 사용이 효율적!)
그러나 문자열을 수정할 때마다 내부 버퍼를 재할당하기 때문에 성능 저하의 우려가 있습니다.
2-2. StringBuffer 예시
public class Main {
public static void main(String[] args) {
// StringBuffer 객체 생성
StringBuffer sb = new StringBuffer("Hello");
// append() 메서드로 문자열 추가
sb.append(" World!");
System.out.println(sb.toString());
// delete() 메서드로 문자열 삭제
sb.delete(5, 11);
System.out.println(sb.toString());
// insert() 메서드로 문자열 삽입
sb.insert(5, " World");
System.out.println(sb.toString());
// replace() 메서드로 문자열 교체
sb.replace(6, 11, "there");
System.out.println(sb.toString());
// reverse() 메서드로 문자열 뒤집기
sb.reverse();
System.out.println(sb.toString());
}
}
<출력>
Hello World!
Hello!
Hello World!
Hello there!
!ereht olleH
문자열 추가 (인스턴스의 끝) |
문자열 추가 (원하는 위치) |
문자열 삭제 |
문자열 역순 | 문자열 추출 | 문자 추출 | 문자열 위치 추출 |
append | insert | delete | reverse | substring | charAt | indexOf |
2-2-1. capacity()
현재 StringBuffer 객체의 *용량(capacity) 반환합니다. (*용량 : StringBuffer 객체에 문자열을 추가할 수 있는 최대 길이)
StringBuffer sb1 = new StringBuffer();
System.out.println(sb1.capacity());
StringBuffer sb2 = new StringBuffer("Hello");
System.out.println(sb2.capacity());
<출력>
16
21
2-2-2. ensureCapacity
객체의 용량을 증가시킬 때 사용하는 메서드입니다. 지정해준 값 이상으로 증가시킵니다.
StringBuffer sb = new StringBuffer();
System.out.println(sb.capacity());
sb.ensureCapacity(30);
System.out.println(sb.capacity());
<출력>
16
34
30으로 지정했으나, 지정된 값 이상으로 증가하는 것을 확인할 수 있습니다.
용량이 증가하는 시점과 얼마나 증가하는지는 실제 버퍼를 사용할 때 중요한 요소 중 하나지만,
이 부분은 StringBuffer 클래스에서 자체적으로 관리하고 있기 때문에 사용자가 직접 조절할 수는 없습니다.
미리 정해진 알고리즘에 따라 동작한다는 것만 알고 계시면 좋을 듯합니다.
2-2-3. setLength
용량이 아닌 길이(문자열의 길이)를 지정된 값으로 설정합니다.
만약 StringBuffer 객체의 현재 길이가 지정된 값보다 작다면, 뒤에 공백을 추가하여 지정된 길이로 설정합니다.
StringBuffer sb1 = new StringBuffer("Hello Java");
sb1.setLength(15);
String str1 = sb1.toString();
System.out.println(str1);
<출력>
Hello Java (Hello Java\0\0\0\0\0)
만약 StringBuffer 객체의 현재 길이가 지정된 값보다 크다면, 지정된 길이까지만 잘라냅니다.
StringBuffer sb2 = new StringBuffer("Hello Java");
sb2.setLength(5);
String str2 = sb2.toString();
System.out.println(str2);
<출력>
Hello
3. StringBuilder란?
StringBuffer와 같이 문자열 수정이 가능한 클래스입니다.
StringBuilder는 Java5부터 도입되어 Java9에서는 개선된 기능을 제공하고 있습니다.
3-1. StringBuilder 특징
StringBuffer와 달리 동기화를 제공하지 않아 멀티스레드 환경에서 안전하지 않습니다.
(단일 스레드 환경에서는 StringBuffer보다 빠름!)
그러나 문자열을 동적으로 생성하고, 조작하거나 연산이 많은 경우에는 성능 향상을 위해 사용합니다.
3-2. StringBuilder 예시
위 2-3의 StringBuffer의 내용을 참고하세요!
❓ 그럼 StringBuilder - StringBuffer 언제 누굴 사용해야 하나요?
단일 스레드 환경에서 문자열 처리를 할 때는 (동기화를 보장하지 않는) StringBuilder를,
멀티 스레드 환경에서 문자열 처리를 할 때는 (*동기화를 보장하는) StringBuffer를 사용하는 것이 좋습니다.
*동기화 : 여러 스레드가 공유하는 자원에 대한 접근을 조율하는 것
(예 : 다수의 스레드가 StringBuffer의 append() 메서드를 호출하더라도, 각 스레드는 순차적으로 접근하고 변경할 수 있도록 보호합니다.)