[JAVA] 문자열 클래스 (String, StringBuffer, StringBuilder)

Van·2023년 5월 21일
0

문자열 클래스

Goal

  • JAVA의 3가지 문자열 클래스에 대해 간략하게 알아보자
  • 각 문자열 클래스의 차이점에 대해 설명할 수 있다
  • 상황에 맞게 문자열 클래스를 사용할 수 있다
  • CharSequence에 대해 간략하게 알아보자
  • 각 클래스의 대표적인 method 사용법을 알아보자

Introduction

JAVA에는 문자열 클래스로 String, StringBuffer, StringBuilder 3가지가 있습니다. 상황에 따라서 어떠한 클래스를 사용하느냐에 따라 성능의 차이가 있습니다. 어떤 차이점이 있는지 알아보겠습니다.


String | StringBuffer | StringBuilder

IndexStringStringBufferStringBuilder
Storage AreaHeap or Constant String PoolHeapHeap
ModifableNo(immutable)Yes(mutable)Yes(mutable)
Thread-SafeYesYesNo

String Class

String 객체는 한 번 생성되면 할당된 메모리 공간이 변하지 않습니다. 즉, '+' 연산 또는 concat 메소드를 통해 기존에 생성된 String 객체에 다른 문자열을 붙여도 기존 문자열에 새로운 문자열을 붙이는 것이 아닙니다. 새로운 String 객체를 만든 후, 이 객체에 연결된 문자열을 저장호가, 그 객체를 참조하도록 합니다.

  • 장점
    • String Class의 객체는 Immutable(불변)하기 때문에 단순하게 읽어가는 조회 연산에서는 타 클래스보다 빠르게 읽을 수 있습니다.
    • Immutable(불변)하기 때문에 멀티쓰레드 환경에서 동기화를 신경 쓸 필요가 없습니다. (Thread-Safe)
  • 단점
    • 문자열 연산('+',concat 등)을 많이 사용하는 경우, 더 이상 참조되지 않는 기존 객체는 Garbage Collection(GC)에 의해 제거되야 하기 때문에 성능이 좋지 않습니다.
    • 또한, 문자열 연산이 많아질 때 연산 내부적으로 char 배열을 사용하고, 계속해서 객체를 만드는 OverHead(오버헤드)가 발생하므로 성능이 떨어질 수 밖에 없습니다.

String 클래스 : 문자열 연산이 적고, 조회가 많은 멀티쓰레드 환경에서 좋음

String method

length

length 메소드는 해당하는 문자열의 길이를 int 값을 반환

String string = "String";
int length = string.length(); // 6
// 해당하는 문자열의 길이를 반환

charAt

charAt 메소드는 인자로 전달한 index 값을 char 값을 반환

만약 해당하는 index에 값이 없다면 Exception 발생

String string = "String";

char charAt = string.charAt(1); // "t"
// 인자로 전달한 index의 문자를 반환

char noCharAt = string.charAt(10); // java.lang.StringIndexOutOfBoundsException : String index out of range
// 만약 인자로 전달한 index가 문자열 길이를 넘으면 Exception 발생

substring

substring 메소드는 인자로 int값 1개 또는 2개를 받아서 해당하는 index의 값부터 끝까지 또는 인자로 전달한 index까지의 String 값을 반환

만약 해당하는 index에 값이 없다면 Exception 발생

String string = "String";

String str1 = string.substring(1); // "tring"
// 첫 번째 인자 index부터 끝까지 반환

String str2 = string.substring(1,5); // "trin"
// 두 번째 인자까지 전달하면 해당하는 index까지의 문자열만 반환

String noSearch = string.substring(10); // java.lang.StringIndexOutOfBoundsException : String index out of range
// 만약 인자로 전달한 index가 문자열 길이를 넘으면 Exception 발생

equals

equals 메소드는 인자로 전달 받은 문자열과 같은지 확인하여 boolean 값을 반환

String string = "String";
String string2 = "string";
String string3 = "string3";

boolean equal1 = string.equals(string2); // true
boolean equal2 = string.equals(string3); // false
// 인자로 전달한 값과 같은지 확인

contains

contains 메소드는 인자로 전달 받은 문자열이 포함되어 있는지 확인하여 boolean 값을 반환

String string = "String";

boolean contains1 = string.contains("X"); // false
boolean contains2 = string.contains("ring"); // true
// 인자로 전달한 CharSequence가 포함되어 있는지 확인

toUpperCase, toLowerCase

toUpperCase 메소드는 문자열 전체를 대문자로 변경
toLowerCase 메소드는 문자열 전체를 소문자로 변경

String string = "String";
String upperString = string.toUpperCase(); // "STRING"
String lowerString = string.toLowerCase(); // "string"
// 각각 upper는 문자열 전체를 대문자로, lower는 문자열 전체를 소문자로 변환

replace

replace 메소드는 인자로 String 값 2개를 받아서 첫 번째로 받은 문자열이 있다면 두 번째로 받은 문자열로 변환

만약 첫 번째로 전달받은 문자열과 같은 문자열이 없다면 변환하지 않음

String string = "String";
String replString1 = string.replace("ring","rong"); // "Strong"
String replString2 = string.replace('v','o'); // "Sting"
// 첫 번째 인자로 전달한 문자 또는 문자열이 있다면 두 번째로 전달한 문자열로 변환
// 만약 없다면 따로 변화는 없음

toCharArray

toCharArray 메소드는 문자열을 모두 쪼개어 Char[]로 반환

String string = "String";
char[] charArr = string.toCharArray(); // [S, t, r, i, n, g]
// 문자열을 한 글자씩 char[] 형태로 변환

concat

concat 메소드는 원본 문자열 뒤에 인자로 받은 문자열을 이어 붙임

String string = "String";
String strConcat = string.concat("StrConcat"); // StringStrConcat
// 문자열 뒤에 인자로 전달한 문자열을 이어 붙임

indexOf, lastIndexOf

indexOf, lastIndexOf는 문자열에서 인자로 전달 받은 문자열의 위치를 찾는 메소드로 다른 점은 앞에서부터 찾느냐 뒤에서부터 챶느냐임, 만약 찾았다면 index를 int로 반환하고 찾지 못 한다면 -1을 반환

String string = "String";
int index = string.indexOf("in"); // 3
// 전달한 인자가 있는 index값을 반환

int noIndex = string.indexOf("q"); // -1
// 전달한 인자에 해당하는 값이 없다면 -1 반환

int lastIndex = string.lastIndexOf("g"); // 5
// 전달한 인자를 뒤에서부터 찾아서 index값을 반환

StringBuffer, StringBuilder

StringBuffer와 StringBuilder 클래스는 String과 다르게 mutable(변경가능)합니다. 즉, 문자열 연산에 있어서 클래스를 한번만 만들고(new), 연산이 필요할 때 크기를 변경시켜서 문자열을 변경합니다. 그러므로 문자열 연산이 자주 있을 때 사용하면 성능이 좋습니다.

StringBuffer와 StringBuilder 클래스가 제공하는 메소드는 서로 동일합니다. 그렇다면 두 클래스의 차이점은 바로 Synchronized(동기화)여부입니다.

StringBuffer는 각 메소드별로 Synchronized Keyword가 존재하여, Multi-Thread 환경에서도 동기화를 지원하여 Thread-Safe합니다.

반면, StringBuilder는 동기화를 보장하지 않습니다. 하지만 StringBuilder는 Single-Thread 환경에서 동기화를 고려하지 않기 때문에 StringBuffer에 비해 연산 처리가 빠릅니다.

그렇기 때문에 Multi-Thread 환경이라면 값 동기화 보장을 위해 StringBuffer를 사용하고, Single-Thread 환경이라면 StringBuilder를 사용하는 것이 좋습니다.

StringBuffer, StringBuilder method

StringBuffer, StringBuilder 메소드는 모두 같기 때문에 아래에서는 Single Thread에서 유리한 StringBuilder로 설명하겠습니다.

append

append 메소드는 인자로 전달받은 문자열을 뒤에 추가한다

StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("b"); // b
stringBuilder.append("u"); // bu
stringBuilder.append("i"); // bui
stringBuilder.append("l"); // buil
stringBuilder.append("d"); // build
stringBuilder.append("er"); // builder
// 뒤에 인자로 받은 문자열을 추가한다.

delete

delete 메소드는 인자로 int 값 2개를 받아서 첫 번째로 받은 값 index부터 두 번째로 받은 index 값까지 문자를 삭제함

만약 해당하는 index에 값이 없다면 Exception 발생

StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("builder"); // builder
stringBuilder.delete(0,2); // ilder
// 첫 번째 인자로 받은 index부터 두 번째 인자로 받은 index까지 문자를 삭제

stringBuilder.delete(12,2); 
// java.lang.StringIndexOutOfBoundsException : start 12, end 2, length 7
// 만약 인자로 전달한 index가 문자열 길이를 넘으면 Exception 발생

insert

insert 메소드는 첫 번째 인자로 int 값, 두 번째 인자로 문자열을 받아서 첫 번째로 받은 값의 index에 두 번째로 받은 값 문자열을 삽입

만약 해당하는 index에 값이 없다면 Exception 발생

StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("builder"); // builder
stringBuilder.insert(0,"str "); // str builder
// 첫 번째 인자로 받은 index부터 두 번째 인자로 받은 문자열을 삽입

stringBuilder.insert(12,"str "); 
// java.lang.StringIndexOutOfBoundsException : offset 12,length 7
// 만약 인자로 전달한 index가 문자열 길이를 넘으면 Exception 발생

reverse

reverse 메소드는 현재 문자열을 뒤집는다.

StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("builder"); // builder
stringBuilder.reverse(); // redliub

replace

replace 메소드는 문자열을 교체하는 메소드입니다.
첫 번째 인자는 int 값으로 교체를 시작하는 index 값
두 번째 인자는 int 값으로 교체를 끝내는 index 값
세 번째 인자는 String 값으로 교체하려는 문자열입니다.

만약 해당하는 index에 값이 없다면 Exception 발생

StringBuilder stringBuilder = new StringBuilder();
stringBuilder.replace(0,stringBuilder.length() + 1,"new String Builder"); // new String Builder
// 첫 번째 인자로 받은 index부터 두 번째 인자로 받은 인덱스까지 세 번째 인자의 값으로 대체

stringBuilder.replace(12,stringBuilder.length() + 1,"new String Builder"); // new String Builder
// java.lang.StringIndexOutOfBoundsException: start 12, end 11, length 11
// 만약 인자로 전달한 index가 문자열 길이를 넘으면 Exception 발생

CharSequence

CharSequence는 JAVA에서 문자열을 나타내는 인터페이스로 문자열 시퀀스에 대한 일반적인 동작을 정의한다. String, StringBuilder, StringBuffer 모두 CharSequence를 구현하고 있어, 이들 모두 CharSequence로 사용 될 수 있습니다.

CharSequence를 사용하면 다양한 문자열 구현체를 동일한 인터페이스로 다룰 수 있어 코드의 유연성과 호환성을 높일수 있습니다.
위의 method 예제에서 인자로 여러 문자열을 전달이 가능했던 이유는 3가지 클래스 모두 CharSequnece를 구현하고 있었기 때문에 가능합니다.

public final class String 
	implements java.io.Serializable, Comparable<String>, CharSequence

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, Comparable<StringBuilder>, CharSequence
    
public final class StringBuffer
    extends AbstractStringBuilder
    implements Serializable, Comparable<StringBuffer>, CharSequence

위의 선언부를 확인해보면 모두 CharSequence를 구현한 것을 확인 할 수 있다.


Conclusion

String Class는 JDK1.5버전 이전에는 문자열연산('+',concat)을 할 때에는 조합된 문자열을 새로운 메모리에 할당하여 참조함으로 인해서 성능상의 이슈가 있었습니다. 그러나 JDK1.5 이후 버전에서는 String 클래스를 활용해도 StringBuilder와 성능상의 차이가 없어졌습니다. 하지만 반복 루프를 사용해서 문자열을 더할 때에는 객체를 계속 추가한다는 사실에는 변함이 없습니다.

String Class를 쓰는 대신, Thread와 관련이 있으면 StringBuffer를, Thread 안전 여부와 상관이 없으면 StringBuilder를 사용하는 것을 권장합니다.

단순하게 성능만 본다면 연산이 많은 경우, StringBuilder > StringBuffer >>> String이라고 합니다.


참조
https://github.com/GimunLee/tech-refrigerator/blob/master/Language/JAVA/%EB%AC%B8%EC%9E%90%EC%97%B4%20%ED%81%B4%EB%9E%98%EC%8A%A4.md#%EB%AC%B8%EC%9E%90%EC%97%B4-%ED%81%B4%EB%9E%98%EC%8A%A4

profile
그럭저럭 어렵지 않게 Smile!

0개의 댓글