본문 바로가기
자바/개인 정리

String 클래스 총정리(feat. StringBuilder, StringBuffer, Arrays)

by limdae94 2025. 1. 11.

String

1. String의 기본 개념

  • 정의: String은 문자의 배열로, 문자열 클래스
  • 자바의 String 클래스: java.lang 패키지에 속하며, 기본적으로 import 없이 사용 가능
  • 불변성: String 객체는 생성된 후 값을 변경할 수 없음. 값을 변경하면 새로운 객체가 생성

2. String 생성 방법

  1. 리터럴 방식
    • 상수 풀(Constant Pool)에 저장
    • 동일한 값이 있다면, 새로운 객체를 생성하지 않고 재사용
    String str1 = "Hello";
  2. new 키워드 사용
    • 힙 영역에 새 객체를 생성
    • 값이 동일하더라도 새로운 객체 생성
    String str2 = new String("Hello");

3. String의 주요 특징

  • Immutable(불변): 문자열을 변경하는 작업은 새로운 객체를 생성

3.1참조 비교와 값 비교

  • ==: 참조값(객체의 메모리 주소)을 비교
  • equals(): 값 비교 (문자열 내용 비교)

Java의 String Pool, 리터럴과 객체 생성 방식의 차이, 참조값과 내용 비교 방법을 이해한다. 이를 통해 메모리 효율성과 동작 원리를 학습할 수 있다.

public class Main {

    public static void main(String[] args) {
        String s1 = "This is a Literal String test"; 
        String s2 = "This is a Literal String test"; 

        System.out.println("s1 == s2: " + s1 == s2); // false
        System.out.println("s1 == s2: " + (s1 == s2)); // s1 == s2: true
        System.out.println("s1.equals(s2): " + s1.equals(s2)); // s1.equals(s2): true
        System.out.println(s1.getClass().getName() + "@"
                + Integer.toHexString(s1.hashCode()));// java.lang.String@544771c5
        System.out.println(s2.getClass().getName() + "@"
                + Integer.toHexString(s2.hashCode()));// java.lang.String@544771c5
        System.out.println(System.identityHashCode(s1)); // 51228289
        System.out.println(System.identityHashCode(s2)); // 51228289

        String s3 = new String("This is a Object String test"); 
        String s4 = new String("This is a Object String test"); 

        System.out.println("s3 == s4: " + s3 == s4); // false
        System.out.println("s3 == s4: " + (s3 == s4)); // s3 == s4: false
        System.out.println("s3.equals(s4): " + s3.equals(s4)); // s3.equals(s4): true
        System.out.println(s3.getClass().getName() + "@"
                + Integer.toHexString(s3.hashCode())); // java.lang.String@a6fb9bd3
        System.out.println(s4.getClass().getName() + "@"
                + Integer.toHexString(s4.hashCode())); // java.lang.String@a6fb9bd3
        System.out.println(System.identityHashCode(s3)); // 455896770
        System.out.println(System.identityHashCode(s4)); // 1323165413

        String s5 = "This is a test";
        String s6 = new String("This is a test");

        System.out.println("s5 == s6: " + (s5 == s6)); // s5 == s6: false
        System.out.println("s5 == s6: " + s5.equals(s6)); // s5 == s6: true
        System.out.println(s5.getClass().getName() + "@"
                + Integer.toHexString(s5.hashCode())); // java.lang.String@544771c5
        System.out.println(s6.getClass().getName() + "@"
                + Integer.toHexString(s6.hashCode())); // java.lang.String@544771c5
        System.out.println(System.identityHashCode(s5)); // 511754216
        System.out.println(System.identityHashCode(s6)); // 1721931908
    }
}

3.2 Literal String 생성

문자열 리터럴은 String Pool에 저장되고, 동일한 내용의 리터럴(s2)은 새로운 객체를 생성하지 않고 같은 객체를 참조할 수 있다. s1, s2는 동일한 리터럴을 참조하므로 같은 메모리 공간을 공유한다.

String s1 = "This is a Literal String test"; 
String s2 = "This is a Literal String test"; 

3.3 연산자 우선순위

연산자 우선순위(Operator Priority)에 의해 반드시 소괄호()를 작성해야만 한다. + 연산자는 == 연산자보다 우선순위가 높기 때문에 의도와 다르게 s1 == s2부터 실행되지 않고, "s1 == s2: " + s1 가 먼저 실행되어 원치 않은 수행 결과를 출력한다.

System.out.println("s1 == s2: " + s1 == s2); // false

3.4 == 연산자

== 연산자는 기본 자료형(Primitive Type)인 경우와 참조 자료형(Reference Type)인 경우에 비교 방식이 다르다.

  • 기본 자료형 ==: 값을 비교
  • 참조 자료형 ==: 참조값(객체의 메모리 주소)을 비교
System.out.println("s1 == s2: " + (s1 == s2)); // s1 == s2: true

두 문자열이 완전히 일치하는 문자열 리터럴을 == 연산자로 비교하면 ture이다.

3.5 equals() 메서드

equals()메서드는 객체의 내용(값)을 비교한다. 기본적으로 Object 클래스에서 정의된 equals()==와 동일하게 참조값을 비교한다. 하지만, String, Integer 등 일부 클래스에서는 equals()가 내용(값) 비교로 오버라이드되어 있다.

System.out.println("s1.equals(s2): " + s1.equals(s2)); // s1.equals(s2): true

String 클래스 내부에 오버라이드된 equals() 메서드로s1, s2 두 문자열의 값이 동일한지 비교하면 true이다.

3.6 Object.equals()

Object 클래스는 모든 클래스의 최상위 클래스이다. 기본적으로 Objectequals() 메서드는 참조값(메모리 주소)를 비교한다.

// Object 클래스 내부
    public boolean equals(Object obj) {
        return (this == obj); // 1단계: 참조 비교
    }

여기서 this == obj두 객체가 동일한 메모리 주소를 참조하는지 확인하는 연산이다. 따라서, Object 클래스의 equals()는 기본적으로 값이 아니라 객체의 동일성(Identity)을 비교한다.

3.7 String.equals()

String 클래스는 Objectequals()를 오버라이드했다. Object 클래스의 기본 구현은 참조 비교(this == obj)만 수행하지만, String 클래스는 이를 오버라이드하여 타입과 값 비교를 수행하도록 동작을 변경했다. equals()Object의 메서드와 비슷해 보이지만, String 내부에서는 타입과 값을 확인하는 추가 논리가 포함되어 있다.

public boolean equals(Object anObject) {
    if (this == anObject) { // 1단계: 참조 비교
        return true;
    }
    // 2단계: 값 비교
    return (anObject instanceof String aString) // 타입 확인
            && (!COMPACT_STRINGS || this.coder == aString.coder) // 내부 최적화 비교
            && StringLatin1.equals(value, aString.value); // 실제 값 비교
}
  1. 참조 비교 (this == anObject)
    두 객체가 동일한 메모리 주소를 참조하면 true를 반환한다.
  2. 타입 확인 (anObject instanceof String)
    anObjectString 타입인지 확인한다. (타입이 다르면 false를 반환)
  3. 값 비교 (StringLatin1.equals(value, aString.value))
    String 객체의 내부 값 배열 (value)을 비교한다. valuechar[] 또는 byte[] 형태로 저장된 실제 문자열 데이터이다.

3.8 Object.toString()

Object 클래스의 toString() 메서드의 기본 hashCode() 구현은 데이터 값을 이용해 계산된 정수 값이다. 즉, 해시코드는 데이터 값에 기반하여 생성된 고유 값으로 메모리 주소, 참조값을 직접적으로 나타내지 않는다. 하지만, String 클래스에서 toString()은 문자열 자체를 반환하도록 오버라이드되어 있다.

// Object 클래스 내부
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

3.9 String.toString()

String 클래스는 불변(Immutable)으로 설계되어 있다. 따라서 생성된 이후 상태를 변경할 수 없는 객체이므로, String 클래스의 toString()은 객체의 데이터인 문자열 자체(this)를 그대로 반환한다.

// String 클래스 내부
public String toString() {
    return this;
}

3.10 String.toString() 실행 영역

아래 코드를 통해 String.toString()의 실행에서 각 영역(메서드 영역, 힙 영역, 스택 영역)에 데이터가 어떻게 저장되는지, 그리고 순서를 자세히 알아보자. new 키워드로 생성된 String 인스턴스는 서로 전혀 다른 객체이기 때문에 서로 다른 힙 영역을 갖는다.

public class Main {
    public static void main(String[] args) {
        // Step 1: 힙 영역에 새로운 String 객체 생성
        String str = new String("Hello"); 

        // Step 2: toString() 호출 - 동일한 객체를 참조
        String str1 = str.toString();

        // Step 3: new String() 호출 - 새로운 객체 생성
        String str2 = new String(str);

        // Comparisons
        System.out.println("str == str1: " + (str == str1));      // 참조 비교
        System.out.println("str.equals(str1): " + str.equals(str1)); // 값 비교
        System.out.println("str == str2: " + (str == str2));      // 참조 비교
        System.out.println("str1 == str2: " + (str1 == str2));    // 참조 비교
        System.out.println("str.equals(str2): " + str.equals(str2)); // 값 비교
    }
}
// 실행 결과
str == str1: true
str.equals(str1): true
str == str2: false
str1 == str2: false
str.equals(str2): true

1. String str = new String("Hello");

  • "Hello" 문자열 리터럴이 String Pool에 저장된다.
  • new String("Hello")는 힙 영역에 새로운 String 객체를 생성하고, 내부에 "Hello" 값을 복사한다.
  • 스택 영역: str 변수는 힙 객체를 참조한다.

2. String str1 = str.toString();

  • toString() 메서드는 현재 객체(this)를 반환한다.
  • 따라서, str1str과 동일한 힙 객체를 참조한다.
  • 새로운 객체가 생성되지 않는다.

3. String str2 = new String(str);

  • new String(str)는 힙 영역에 새로운 String 객체를 생성한다.
  • 새 객체는 str의 값을 복사하여 내부적으로 동일한 문자열 데이터를 저장한다.
  • str2str과 다른 힙 객체를 참조한다.

비교 결과 및 이유

비교 표현 결과 설명
str == str1 true str1str과 동일한 객체를 참조
str.equals(str1) true equals()는 문자열 내용을 비교하며, 두 객체의 내용이 동일
str == str2 false str2new String(str)로 생성된 새로운 객체
str1 == str2 false str1str2는 서로 다른 힙 객체를 참조
str.equals(str2) true equals()는 문자열 내용을 비교하며, 두 객체의 내용이 동일
  1. toString(): 현재 객체(this)를 반환하므로, 참조값이 동일
  2. new String(): 항상 새로운 객체를 생성하므로 참조값이 다름
  3. ==equals() 차이
    • ==: 참조값(메모리 주소)을 비교
    • equals(): 문자열 내용을 비교

3.11 String.hashCode()

객체의 hashCode()Object 클래스에 정의된 메서드로, 기본적으로 참조값 기반으로 해시코드를 반환한다. 하지만 String, Integer와 같은 클래스는 내용 기반으로 hashCode()를 오버라이드하여, 동일한 내용(값)의 객체는 동일한 해시코드를 반환하도록 설계되었다. 그래서 String 클래스의 hashCode()는 객체의 메모리 주소값이 아니라 문자열(값)을 기반으로 해시코드를 생성한다.

따라서 서로 다른 String 객체라 하더라도 문자열의 내용이 동일하다면 동일한 해시코드를 반환한다.

System.out.println(s1.getClass().getName() + "@"
        + Integer.toHexString(s1.hashCode()));// java.lang.String@544771c5
System.out.println(s2.getClass().getName() + "@"
        + Integer.toHexString(s2.hashCode()));// java.lang.String@544771c5

String은 내용 기반으로 hashCode()를 계산할까?

1. 해시 기반 컬렉션의 활용
HashMap, HashSet 등 해시 기반 자료구조에서 키로 사용될 때, 동일한 문자열 값을 가진 String 객체들이 같은 해시코드를 가져야 제대로 작동한다.

Map<String, Integer> map = new HashMap<>();
map.put(new String("key"), 1);
System.out.println(map.get(new String("key"))); // 1 (내용 기반 비교)

2. 문자열 불변성
String은 불변(immutable) 객체이므로, 한 번 계산된 해시코드는 변경되지 않는다. 이는 효율적인 캐싱과 재사용을 가능하게 한다.

3.12 System.identityHashCode()

String의 실제 참조값 기반 해시코드는 어떻게 확인할까?

객체의 참조값을 확인하려면 System.identityHashCode()를 사용한다. s1, s2는 동일한 문자열 리터럴을 참조하여 참조값이 동일하고, s3, s4는 문자열(값)은 동일하더라도 참조하는 String 인스턴스가 서로 다르기 때문에 참조값이 다르다.

String s1 = "This is a Literal String test"; // Literal
String s2 = "This is a Literal String test"; // Literal
...
System.out.println(System.identityHashCode(s1)); // 51228289
System.out.println(System.identityHashCode(s2)); // 51228289
String s3 = new String("This is a Object String test"); // new String()
String s4 = new String("This is a Object String test"); // new String()
...
System.out.println(System.identityHashCode(s3)); // 455896770
System.out.println(System.identityHashCode(s4)); // 1323165413

3.13 요약

String은 불변(Immutable)

  • 문자열을 변경하면 새로운 객체가 생성된다.

참조와 값 비교

  • ==: 참조값(메모리 주소)을 비교(동일성)
  • equals(): 문자열의 값을 비교(동등성)

리터럴과 객체 생성 방식

  • 문자열 리터럴은 String Pool에 저장되어 메모리를 효율적으로 사용한다.
  • new String()은 항상 새로운 객체를 생성한다.

연산자 우선순위 주의

  • + 연산자가 ==보다 우선순위가 높으므로 괄호로 연산 순서를 명시해야 한다.

String의 equals()

  • Objectequals()를 오버라이드하여 값 비교를 수행한다.
  • 타입 확인 후 내부 데이터 배열을 비교한다.

toString() 동작

  • Object.toString(): 클래스명과 해시코드 반환
  • String.toString(): 문자열 자체를 반환(데이터 그대로)
  • String.toString()은 현재 객체(this)를 반환하여 동일한 참조값을 가짐

String 객체의 저장 영역

  • 리터럴: String Pool에 저장, 동일한 문자열 리터럴은 동일한 객체를 참조
  • new String(): 항상 새로운 힙 객체를 생성

hashCode()

  • String 클래스는 내용(값)을 기반으로 해시코드를 오버라이드
  • 동일한 내용의 문자열은 동일한 해시코드를 가짐
  • HashMap, HashSet에서 키로 사용 시, 동일한 내용의 문자열은 같은 해시코드를 반환해 올바르게 작동

System.identityHashCode()

  • 객체의 실제 참조값 기반 해시코드를 확인
  • 동일한 리터럴은 참조값이 동일, new String()은 참조값이 다름

4. String 메서드

String 클래스의 주요 메서드들을 시그니처와 함께 나열한 목록이다.

4.1 문자열 길이 및 정보

int length(); 
boolean isEmpty(); 
boolean isBlank(); 
char charAt(int index);
int codePointAt(int index);
int codePointBefore(int index);
int codePointCount(int beginIndex, int endIndex);

1. int length(): 문자열의 길이를 반환

String str = "Hello";
System.out.println(str.length()); // 출력: 5

2. boolean isEmpty(): 문자열이 비어있는지 확인. 문자열의 길이가 0이면 true를 반환

String str1 = "";
String str2 = " ";
System.out.println(str1.isEmpty()); // 출력: true
System.out.println(str2.isEmpty()); // 출력: false

3. boolean isBlank(): 문자열이 비어있거나 공백 문자(스페이스, 탭, 줄바꿈 등)로만 이루어져 있는지 확인

String str1 = "";
String str2 = " ";
String str3 = "\n\t";
System.out.println(str1.isBlank()); // 출력: true
System.out.println(str2.isBlank()); // 출력: true
System.out.println(str3.isBlank()); // 출력: true

4. char charAt(int index): 문자열에서 주어진 인덱스에 위치한 문자를 반환
(0부터 시작하는 인덱스를 사용. 범위를 벗어나면 IndexOutOfBoundsException이 발생)

String str = "Hello";
System.out.println(str.charAt(0)); // 출력: H
System.out.println(str.charAt(4)); // 출력: o

5. int codePointAt(int index): 인덱스에서 Unicode 코드 포인트를 반환
(다중 바이트 문자에 대해 올바르게 처리)

String str = "A💖B";
System.out.println(str.codePointAt(1)); // 출력: 128150 (💖의 유니코드 값)

6. int codePointBefore(int index): 인덱스 앞에 있는 문자에 대한 Unicode 코드 포인트를 반환

String str = "A💖B";
System.out.println(str.codePointBefore(2)); // 출력: 128150 (💖의 유니코드 값)

7. int codePointCount(int beginIndex, int endIndex): 시작 인덱스부터 종료 인덱스까지의 수를 반환

String str = "A💖B";
System.out.println(str.codePointCount(0, str.length())); // 출력: 3 (A, 💖, B)

8. isEmpty vs isBlank 차이점

메서드 동작 예제
isEmpty 문자열이 길이가 0인지 확인 (""true) "".isEmpty() -> true
isBlank 문자열이 비어있거나 공백 문자만 포함 (" ", "\n\t" 등도 true) " ".isBlank() -> true
  • isEmpty문자열 길이만 검사하며 공백은 포함된 것으로 간주
  • isBlank공백 문자가 포함된 문자열도 비어있는 것으로 간주

9. Unicode 코드 포인트

  • 유니코드 코드 포인트는 각 문자를 전 세계적으로 고유하게 식별하는 정수 값(U+0000 ~ U+10FFFF)
  • 16진수로 표시 (U+ 접두사 사용. A: U+0041, : U+D55C, 💖: U+1F496)

Java에서는 UTF-16 인코딩을 사용하므로, 대부분의 문자(U+0000~ U+FFFF)는 1개의 코드 유닛으로 표현되고, U+10000 이상 문자(예: 이모지)는 2개의 코드 유닛(Surrogate Pair)으로 표현된다. 예를 들어 💖 (128150(U+1F496))는 UTF-16에서 D83DDC96 두 개의 코드 유닛으로 저장된다.

문자 Unicode 코드 포인트 UTF-16 코드 유닛
A U+0041 0041
U+D55C D55C
💖 U+1F496 D83D DC96 (Surrogate Pair)
  • 다국어 지원: 다양한 언어의 문자를 고유하게 식별하여, 전 세계 언어를 지원
  • 정확한 문자 처리: UTF-16에서는 하나의 문자(예: 이모지)가 여러 코드 유닛으로 표현될 수 있으므로, 정확한 문자 길이 계산과 비교를 위해 코드 포인트를 사용해야 함

4.2 문자열 검색

boolean contains(CharSequence s);
boolean startsWith(String prefix);
boolean startsWith(String prefix, int offset);
boolean endsWith(String suffix);
int indexOf(int ch);
int indexOf(int ch, int fromIndex);
int indexOf(String str);
int indexOf(String str, int fromIndex);
int lastIndexOf(int ch);
int lastIndexOf(int ch, int fromIndex);
int lastIndexOf(String str);
int lastIndexOf(String str, int fromIndex);

1. boolean contains(CharSequence s): 문자열에 지정된 문자 시퀀스(CharSequence)가 포함되어 있는지 확인

String str = "Hello, World!";
System.out.println(str.contains("World")); // true
System.out.println(str.contains("Java"));  // false

2. boolean startsWith(String prefix): 문자열이 지정된 접두사(prefix)로 시작하는지 확인

String str = "Hello, World!";
System.out.println(str.startsWith("Hello")); // true
System.out.println(str.startsWith("World")); // false

3. boolean startsWith(String prefix, int offset):
문자열이 지정된 위치(offset)에서 시작하여 특정 접두사(prefix)와 일치하는지 확인

String str = "Hello, World!";
System.out.println(str.startsWith("World", 7)); // true
System.out.println(str.startsWith("Hello", 7)); // false

4. boolean endsWith(String suffix): 문자열이 지정된 접미사(suffix)로 끝나는지 확인

String str = "Hello, World!";
System.out.println(str.endsWith("World!")); // true
System.out.println(str.endsWith("Hello"));  // false

5. int indexOf(int ch): 문자열에서 지정된 문자(ch)가 처음 등장하는 위치(인덱스)를 반환(없으면 -1을 반환)

String str = "Hello, World!";
System.out.println(str.indexOf('o')); // 4
System.out.println(str.indexOf('z')); // -1

6. int indexOf(int ch, int fromIndex):
문자열에서 지정된 위치(fromIndex)부터 시작하여 지정된 문자(ch)가 처음 등장하는 위치를 반환

String str = "Hello, World!";
System.out.println(str.indexOf('o', 5)); // 8
System.out.println(str.indexOf('o', 9)); // -1

7. int indexOf(String str)
문자열에서 지정된 문자열(str)이 처음으로 등장하는 위치(인덱스)를 반환(없으면 -1을 반환)

String str = "Hello, World!";
System.out.println(str.indexOf("World")); // 7
System.out.println(str.indexOf("Java"));  // -1

8. int indexOf(String str, int fromIndex)
문자열에서 지정된 위치(fromIndex)부터 시작하여 지정된 문자열(str)이 처음 등장하는 위치를 반환

String str = "Hello, Hello!";
System.out.println(str.indexOf("Hello", 1));  // 7
System.out.println(str.indexOf("Hello", 8));  // -1

9. int lastIndexOf(int ch):
문자열에서 지정된 문자(ch)가 마지막으로 등장하는 위치(인덱스)를 반환(없으면 -1을 반환)

String str = "Hello, World!";
System.out.println(str.lastIndexOf('o')); // 8
System.out.println(str.lastIndexOf('z')); // -1

10. int lastIndexOf(int ch, int fromIndex)
문자열에서 지정된 위치(fromIndex)부터 역방향으로 검색하여 지정된 문자(ch)가 마지막으로 등장하는 위치를 반환

String str = "Hello, World!";
System.out.println(str.lastIndexOf('o', 7)); // 4
System.out.println(str.lastIndexOf('o', 3)); // -1

11. int lastIndexOf(String str)
문자열에서 지정된 문자열(str)이 마지막으로 등장하는 위치(인덱스)를 반환(없으면 -1을 반환)

String str = "Hello, World! Hello!";
System.out.println(str.lastIndexOf("Hello")); // 14
System.out.println(str.lastIndexOf("Java"));  // -1

12. int lastIndexOf(String str, int fromIndex)
문자열에서 지정된 위치(fromIndex)부터 역방향으로 검색하여 지정된 문자열(str)이 마지막으로 등장하는 위치를 반환

String str = "Hello, World! Hello!";
System.out.println(str.lastIndexOf("Hello", 13)); // 0
System.out.println(str.lastIndexOf("Hello", 5));  // 0

4.3 문자열 비교

boolean equals(Object anObject);
boolean equalsIgnoreCase(String anotherString);
int compareTo(String anotherString);
int compareToIgnoreCase(String str);
boolean matches(String regex);
boolean regionMatches(int toffset, String other, int ooffset, int len);
boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len);

1. boolean equals(Object anObject): 문자열의 내용(값)이 주어진 객체와 동일한지 확인(대소문자를 구분)

String str1 = "Hello";
String str2 = "Hello";
String str3 = "hello";

System.out.println(str1.equals(str2)); // true
System.out.println(str1.equals(str3)); // false

2. boolean equalsIgnoreCase(String anotherString): 문자열의 내용이 주어진 문자열과 동일한지 확인(대소문자를 구분하지 않음)

String str1 = "Hello";
String str2 = "hello";

System.out.println(str1.equalsIgnoreCase(str2)); // true
System.out.println(str1.equalsIgnoreCase("HELLO")); // true
System.out.println(str1.equalsIgnoreCase("World")); // false

3. int compareTo(String anotherString): 사전 순서(lexicographical order)로 두 문자열을 비교

  • 반환값
    • 0: 문자열이 같음
    • 1: 현재 문자열이 비교 대상보다 큼
    • -1: 현재 문자열이 비교 대상보다 작음
String str1 = "apple";
String str2 = "banana";

System.out.println(str1.compareTo(str2)); // -1 (apple < banana)
System.out.println(str2.compareTo(str1)); // 1  (banana > apple)
System.out.println(str1.compareTo("apple")); // 0

4. int compareToIgnoreCase(String str): 사전 순서로 문자열을 비교하지만 대소문자를 구분하지 않음

String str1 = "apple";
String str2 = "Apple";

System.out.println(str1.compareToIgnoreCase(str2)); // 0
System.out.println("banana".compareToIgnoreCase("Apple")); // 1 (banana > apple)
System.out.println("apple".compareToIgnoreCase("Banana")); // -1 (apple < banana)

5. boolean matches(String regex): 문자열이 주어진 정규식(regex)과 일치하는지 확인

String str = "abc123";

System.out.println(str.matches("[a-z]+\\d+")); // true (소문자 + 숫자 조합)
System.out.println(str.matches("\\d+")); // false (숫자만 포함되지 않음)

6. boolean regionMatches(int toffset, String other, int ooffset, int len)
현재 문자열과 다른 문자열의 지정된 영역이 동일한지 비교(대소문자를 구분)

  • toffset: 현재 문자열의 시작 인덱스
  • other: 비교 대상 문자열
  • ooffset: 비교 대상 문자열의 시작 인덱스
  • len: 비교할 길이
String str1 = "HelloWorld";
String str2 = "World";

System.out.println(str1.regionMatches(5, str2, 0, 5)); // true (Hello**World**)
System.out.println(str1.regionMatches(0, str2, 0, 5)); // false

7. boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)
현재 문자열과 다른 문자열의 지정된 영역이 동일한지 비교(대소문자를 무시할지 여부 설정이 가능)

  • ignoreCase: 대소문자 구분 여부 설정 (true이면 구분하지 않음)
String str1 = "HelloWorld";
String str2 = "world";

System.out.println(str1.regionMatches(true, 5, str2, 0, 5)); // true (대소문자 무시)
System.out.println(str1.regionMatches(false, 5, str2, 0, 5)); // false (대소문자 구분)

요약

  • equals vs equalsIgnoreCase: 대소문자 구분 여부
  • compareTo vs compareToIgnoreCase: 사전 순 비교에서 대소문자 구분 여부
  • matches: 문자열과 정규식 비교
  • regionMatches: 문자열의 특정 영역 비교 (대소문자 구분 가능)

4.4 문자열 변환 및 복사

String substring(int beginIndex);
String substring(int beginIndex, int endIndex);
char[] toCharArray();
byte[] getBytes();
byte[] getBytes(Charset charset);
byte[] getBytes(String charsetName) throws UnsupportedEncodingException;
void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin);

1. String substring(int beginIndex): 문자열에서 지정된 시작 인덱스(beginIndex)부터 끝까지의 부분 문자열을 반환

String str = "Hello, World!";
System.out.println(str.substring(7)); // "World!"

2. String substring(int beginIndex, int endIndex):
문자열에서 지정된 시작 인덱스(beginIndex)부터 종료 인덱스(endIndex) 전까지의 부분 문자열을 반환

String str = "Hello, World!";
System.out.println(str.substring(0, 5)); // "Hello"
System.out.println(str.substring(7, 12)); // "World"

3. char[] toCharArray(): 문자열을 char 배열로 변환하여 반환

String str = "Hello";
char[] charArray = str.toCharArray();
System.out.println(Arrays.toString(charArray)); // [H, e, l, l, o]

4. byte[] getBytes(): 플랫폼의 기본 문자 인코딩을 사용하여 문자열을 바이트 배열로 변환
주의: 플랫폼 의존적이므로 특정 인코딩을 지정하는 것을 권장한다.

String str = "Hello";
byte[] byteArray = str.getBytes();
System.out.println(Arrays.toString(byteArray)); // [72, 101, 108, 108, 111] (ASCII 값)

5. byte[] getBytes(Charset charset): 지정된 Charset을 사용하여 문자열을 바이트 배열로 변

String str = "Hello";
byte[] byteArray = str.getBytes(StandardCharsets.UTF_8);
System.out.println(Arrays.toString(byteArray)); // [72, 101, 108, 108, 111] (UTF-8 인코딩)

6. byte[] getBytes(String charsetName): 지정된 문자 인코딩(charsetName)을 사용하여 문자열을 바이트 배열로 변환

try {
    String str = "Hello";
    byte[] byteArray = str.getBytes("UTF-16");
    System.out.println(Arrays.toString(byteArray)); // UTF-16 인코딩 값 출력
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}

7. void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
문자열의 특정 범위(srcBegin ~ srcEnd)를 char 배열로 복사하여, 지정된 배열(dst)의 특정 위치(dstBegin)에 삽입

String str = "Hello, World!";
char[] dst = new char[5];
str.getChars(7, 12, dst, 0);
System.out.println(Arrays.toString(dst)); // [W, o, r, l, d]

요약

  1. substring: 문자열의 특정 부분을 추출
  2. toCharArray: 문자열을 char 배열로 변환
  3. getBytes: 문자열을 바이트 배열로 변환 (인코딩 가능)
  4. getChars: 문자열의 일부를 char 배열에 복사

4.5 문자열 수정

String concat(String str);
String replace(char oldChar, char newChar);
String replace(CharSequence target, CharSequence replacement);
String replaceAll(String regex, String replacement);
String replaceFirst(String regex, String replacement);
String trim();
String strip();
String stripLeading();
String stripTrailing();
String repeat(int count);

1. String concat(String str): 주어진 문자열(str)을 현재 문자열에 연결하여 새 문자열을 반환(원본 문자열은 변경되지 않음)

String str = "Hello";
String result = str.concat(", World!");
System.out.println(result); // "Hello, World!"

2. String replace(char oldChar, char newChar): 문자열 내의 모든 특정 문자(oldChar)를 다른 문자(newChar)로 대체

String str = "Hello, World!";
String result = str.replace('o', '0');
System.out.println(result); // "Hell0, W0rld!"

3. String replace(CharSequence target, CharSequence replacement):
문자열 내의 특정 시퀀스(target)를 다른 시퀀스(replacement)로 대체

String str = "Hello, World!";
String result = str.replace("World", "Java");
System.out.println(result); // "Hello, Java!"

4. String replaceAll(String regex, String replacement)
문자열에서 주어진 정규식(regex)과 일치하는 모든 부분을 지정된 문자열(replacement)로 대체

String str = "a1b2c3";
String result = str.replaceAll("\\d", "X"); // 숫자를 X로 대체
System.out.println(result); // "aXbXcX"

5. String replaceFirst(String regex, String replacement)
문자열에서 주어진 정규식(regex)과 일치하는 첫 번째 부분만 대체

String str = "a1b2c3";
String result = str.replaceFirst("\\d", "X"); // 첫 번째 숫자만 X로 대체
System.out.println(result); // "aXb2c3"

6. String trim()문자열의 양쪽 끝에서 공백(스페이스) 문자를 제거(중간의 공백은 제거되지 않음)

String str = "  Hello, World!  ";
System.out.println(str.trim()); // "Hello, World!"

7. String strip()
문자열의 양쪽 끝에서 모든 유니코드 공백을 제거
(Java 11부터 추가되었으며, trim()과 비슷하지만 더 포괄적임)

String str = "  Hello, World!  ";
System.out.println(str.strip()); // "Hello, World!"

8. String stripLeading(): 문자열의 왼쪽(시작 부분) 공백만 제거

String str = "  Hello, World!  ";
System.out.println(str.stripLeading()); // "Hello, World!  "

9. String stripTrailing(): 문자열의 오른쪽(끝 부분) 공백만 제거합

String str = "  Hello, World!  ";
System.out.println(str.stripTrailing()); // "  Hello, World!"

10. String repeat(int count)
현재 문자열을 지정된 횟수(count)만큼 반복하여 새로운 문자열을 반환(Java 11부터 추가)

String str = "Hi!";
System.out.println(str.repeat(3)); // "Hi!Hi!Hi!"

요약

  • concat: 문자열 연결
  • replace: 특정 문자/문자열 대체
  • replaceAll & replaceFirst: 정규식을 사용한 대체
  • trim & strip: 문자열의 공백 제거 (차이: strip은 유니코드 공백도 제거)
  • stripLeading & stripTrailing: 좌/우 공백 제거
  • repeat: 문자열 반복

4.6 문자열 분리

String[] split(String regex);
String[] split(String regex, int limit);

1. String[] split(String regex): 문자열을 주어진 정규식(regex)을 기준으로 분리하여 문자열 배열로 반환

  • 분리 기준에 해당하는 문자열은 결과에 포함되지 않음
  • 입력 문자열이 분리 기준에 맞지 않으면 원본 문자열을 포함하는 배열을 반환
String str = "apple,banana,grape";
String[] result = str.split(",");
System.out.println(Arrays.toString(result)); // [apple, banana, grape]

2. String[] split(String regex, int limit)
문자열을 주어진 정규식(regex)을 기준으로 분리하여 문자열 배열로 반환(배열의 크기를 제한할 수 있음)

limit 동작:

  • limit > 0: 최대 limit 개의 문자열만 반환. 나머지는 마지막 요소에 포함
  • limit == 0: 모든 구분자를 기준으로 분리. 끝의 빈 문자열은 제거
  • limit < 0: 모든 구분자를 기준으로 분리. 끝의 빈 문자열도 포함
String str = "apple,banana,grape";
String[] result1 = str.split(",", 2);
System.out.println(Arrays.toString(result1)); // [apple, banana,grape]

String[] result2 = str.split(",", 0);
System.out.println(Arrays.toString(result2)); // [apple, banana, grape]

String[] result3 = str.split(",", -1);
System.out.println(Arrays.toString(result3)); // [apple, banana, grape]

추가 예제

1. 기본 분리

String str = "one two three";
String[] result = str.split(" ");
System.out.println(Arrays.toString(result)); // [one, two, three]

2. 정규식 사용

String str = "one1two2three";
String[] result = str.split("\\d"); // 숫자를 기준으로 분리
System.out.println(Arrays.toString(result)); // [one, two, three]

3. 빈 문자열 처리

String str = "apple,,,banana,,grape";
String[] result1 = str.split(",");
System.out.println(Arrays.toString(result1)); // [apple, , , banana, , grape]

String[] result2 = str.split(",", -1);
System.out.println(Arrays.toString(result2)); // [apple, , , banana, , grape, ]

4. limit 예제

String str = "apple,banana,grape";
System.out.println(Arrays.toString(str.split(",", 1))); // [apple,banana,grape]
System.out.println(Arrays.toString(str.split(",", 2))); // [apple, banana,grape]
System.out.println(Arrays.toString(str.split(",", 3))); // [apple, banana, grape]
System.out.println(Arrays.toString(str.split(",", 0))); // [apple, banana, grape]

요약

  1. split(String regex): 주어진 정규식을 기준으로 문자열을 분리
  2. split(String regex, int limit): 분리 후 반환 배열의 크기(limit)를 설정 가능
    • limit > 0: 최대 limit 개의 문자열 반환
    • limit == 0: 모든 구분자로 분리하며, 끝의 빈 문자열 제거
    • limit < 0: 모든 구분자로 분리하며, 끝의 빈 문자열 포함

4.7 문자열 대소문자 변환

String toLowerCase();
String toLowerCase(Locale locale);
String toUpperCase();
String toUpperCase(Locale locale);

1. String toLowerCase(): 문자열을 소문자로 변환한 새로운 문자열을 반환(기본적으로 기본 로케일을 사용)

String str = "Hello, World!";
System.out.println(str.toLowerCase()); // "hello, world!"

2. String toLowerCase(Locale locale): 문자열을 소문자로 변환한 새로운 문자열을 반환(지정된 로케일을 기반으로 변환)
특정 언어 및 지역 설정에 따라 다른 결과를 반환할 수 있음

String str = "HELLO, WORLD!";
// 터키어 로케일에서는 'I'가 'ı'로 변환됨
System.out.println(str.toLowerCase(Locale.forLanguageTag("tr"))); // "hello, world!"

3. String toUpperCase(): 문자열을 대문자로 변환한 새로운 문자열을 반환(기본적으로 기본 로케일을 사용)

String str = "hello, world!";
System.out.println(str.toUpperCase()); // "HELLO, WORLD!"

4. String toUpperCase(Locale locale): 문자열을 대문자로 변환한 새로운 문자열을 반환(지정된 로케일을 기반으로 변환)
특정 언어 및 지역 설정에 따라 다른 결과를 반환할 수 있음

String str = "i̇stanbul";

주요 차이점

기본 로케일 vs 지정된 로케일

  • 기본 메서드 (toLowerCase()toUpperCase())는 시스템의 기본 로케일을 사용
  • 로케일을 지정하면 해당 로케일에 맞는 문자 규칙을 따름
  • 예: 터키어 로케일(Locale.forLanguageTag("tr"))에서는 Iı, iİ로 변환

4.8 문자열 포맷팅

static String format(String format, Object... args);
static String format(Locale l, String format, Object... args);

1. static String format(String format, Object... args)
지정된 형식 문자열(format)과 전달된 인수(args)를 사용하여 서식화된 문자열을 반환

  • 형식 지정자를 사용하여 문자열, 숫자, 날짜 등을 서식화할 수 있다.
  • 기본 로케일을 사용한다.
  • 형식 지정자 주요 예
    • %s: 문자열
    • %d: 10진수 정수
    • %f: 부동소수점
    • %n: 줄바꿈
    • %t: 날짜/시간
String name = "Alice";
int age = 25;
double height = 165.5;

String result = String.format("Name: %s, Age: %d, Height: %.2f cm", name, age, height);
System.out.println(result); // 출력: Name: Alice, Age: 25, Height: 165.50 cm

2. static String format(Locale l, String format, Object... args)

  • 지정된 로케일(l)과 형식 문자열(format), 전달된 인수(args)를 사용하여 서식화된 문자열을 반환한다. 로케일을 지정하면 숫자, 날짜 등 로케일 특유의 형식으로 서식화된다.
import java.util.Locale;

double number = 1234567.89;

// 기본 로케일
String resultDefault = String.format("Number: %,f", number);
System.out.println(resultDefault); // 출력: Number: 1,234,567.890000 (기본 로케일)

// 독일 로케일
String resultGerman = String.format(Locale.GERMANY, "Number: %,f", number);
System.out.println(resultGerman); // 출력: Number: 1.234.567,890000 (독일식)

// 프랑스 로케일
String resultFrench = String.format(Locale.FRANCE, "Number: %,f", number);
System.out.println(resultFrench); // 출력: Number: 1 234 567,890000 (프랑스식)

추가 예제

1. 문자열 서식화

String firstName = "John";
String lastName = "Doe";
System.out.println(String.format("Hello, %s %s!", firstName, lastName)); // 출력: Hello, John Doe!

2. 숫자 서식화

int quantity = 5;
double price = 10.5;
System.out.println(String.format("Total: %d items for $%.2f", quantity, price)); // 출력: Total: 5 items for $10.50

3. 날짜 서식화

import java.util.Date;

Date now = new Date();
System.out.println(String.format("Today is %tA, %<tB %<td, %<tY", now)); 
// 출력: Today is (요일), (월) (날짜), (연도)

4. 로케일 지정

import java.util.Locale;

double amount = 1234567.89;
System.out.println(String.format(Locale.US, "US: %,.2f", amount)); // US: 1,234,567.89
System.out.println(String.format(Locale.GERMANY, "Germany: %,.2f", amount)); // Germany: 1.234.567,89
System.out.println(String.format(Locale.FRANCE, "France: %,.2f", amount)); // France: 1 234 567,89

요약

  1. String.format(String format, Object... args):
    • 문자열을 서식화하여 반환
    • 기본 로케일 사용
  2. String.format(Locale l, String format, Object... args):
    • 문자열을 서식화하며, 지정된 로케일을 사용

주요 활용:

  • 숫자 및 날짜 포맷 지정
  • 로케일에 따른 출력 형식 변경
  • 간결하고 직관적인 서식화 지원

4.9 문자열 유틸리티

static String valueOf(boolean b);
static String valueOf(char c);
static String valueOf(char[] data);
static String valueOf(char[] data, int offset, int count);
static String valueOf(double d);
static String valueOf(float f);
static String valueOf(int i);
static String valueOf(long l);
static String valueOf(Object obj);

1. static String valueOf(boolean b): boolean 값을 문자열로 변환하여 반환

boolean flag = true;
String result = String.valueOf(flag);
System.out.println(result); // "true"

2. static String valueOf(char c): 단일 문자(char)를 문자열로 변환하여 반환

char ch = 'A';
String result = String.valueOf(ch);
System.out.println(result); // "A"

3. static String valueOf(char[] data): 문자 배열 전체를 문자열로 변환하여 반환

char[] chars = {'H', 'e', 'l', 'l', 'o'};
String result = String.valueOf(chars);
System.out.println(result); // "Hello"

4. static String valueOf(char[] data, int offset, int count)
문자 배열의 지정된 범위(offset부터 count 길이)를 문자열로 변환하여 반환

char[] chars = {'H', 'e', 'l', 'l', 'o'};
String result = String.valueOf(chars, 1, 3);
System.out.println(result); // "ell"

5. static String valueOf(double d): double 값을 문자열로 변환하여 반환

double num = 10.5;
String result = String.valueOf(num);
System.out.println(result); // "10.5"

6. static String valueOf(float f): float 값을 문자열로 변환하여 반환

float num = 5.75f;
String result = String.valueOf(num);
System.out.println(result); // "5.75"

7. static String valueOf(int i): int 값을 문자열로 변환하여 반환

int num = 42;
String result = String.valueOf(num);
System.out.println(result); // "42"

8. static String valueOf(long l): long 값을 문자열로 변환하여 반환

long num = 123456789L;
String result = String.valueOf(num);
System.out.println(result); // "123456789"

9. static String valueOf(Object obj): 객체의 toString() 결과를 문자열로 반환(객체가 null이면 "null" 문자열을 반환)

Object obj = new Object();
System.out.println(String.valueOf(obj)); // "java.lang.Object@..."
System.out.println(String.valueOf(null)); // "null"

4.10 문자열 정규화

String intern();

1. String intern(): 문자열 객체를 String Pool에 등록하거나, 이미 존재하는 경우 동일한 객체를 반환(메모리 효율성을 위해 사용)

String str1 = new String("Hello");
String str2 = "Hello";
String str3 = str1.intern();

System.out.println(str1 == str2); // false (str1은 힙에 생성)
System.out.println(str2 == str3); // true (str3은 String Pool에 있는 "Hello"를 참조)

4.11 기타 메서드

int hashCode();
String toString();
Object clone();

1. int hashCode(): 문자열의 해시코드를 반환
문자열의 내용(값)을 기반으로 계산되며, 같은 내용을 가진 문자열은 동일한 해시코드를 가짐

String str = "Hello";
System.out.println(str.hashCode()); // "Hello"의 해시코드 출력

2. String toString(): 문자열의 내용을 반환
String 클래스에서 오버라이드되어 객체의 참조값이 아닌 문자열 자체를 반환

String str = "Hello";
System.out.println(str.toString()); // "Hello"

3. Object clone(): String 클래스는 불변(immutable) 특성으로 인해, 클론을 호출하면 동일한 객체를 반환

String str = "Hello";
String cloneStr = (String) str.clone();
System.out.println(str == cloneStr); // true (같은 객체)

요약

  1. valueOf: 다양한 데이터 타입을 문자열로 변환
  2. intern: 문자열을 String Pool에 등록하거나 기존 문자열 참조 반환
  3. hashCode: 문자열의 내용을 기반으로 해시코드 반환
  4. toString: 문자열 자체 반환
  5. clone: 동일한 객체를 반환 (불변 특성으로 인해)

5. StringBuilder

  • StringBuilderJava의 문자열 처리 클래스로, 변경 가능한 문자열(mutable string)을 다룰 때 사용
  • StringBuilder가변(mutable). (문자열을 수정할 때 새로운 객체를 생성하지 않음)
  • 따라서 문자열 연결, 삽입, 삭제 등 작업에서 메모리와 성능이 효율적

5.1 String vs StringBuilder 비교

특징 String StringBuilder
불변/가변 불변 (Immutable) 가변 (Mutable)
수정 시 동작 새로운 객체 생성 같은 객체에서 변경
성능 수정 작업이 많으면 비효율적 수정 작업에 최적화
멀티스레드 환경 안전하지 않음 안전하지 않음 (동기화 필요 시 StringBuffer 사용)

5.2 StringBuilder 주요 메서드

메서드 설명
append(String str) 문자열을 끝에 추가
insert(int offset, String str) 특정 위치에 문자열 삽입
replace(int start, int end, String str) 특정 범위를 다른 문자열로 대체
delete(int start, int end) 특정 범위의 문자열 삭제
reverse() 문자열을 뒤집음
capacity() 내부 버퍼의 크기 반환
ensureCapacity(int minimumCapacity) 내부 버퍼 크기를 최소값으로 확장
length() 문자열의 현재 길이 반환
setLength(int newLength) 문자열의 길이를 지정
charAt(int index) 지정된 인덱스의 문자 반환
toString() StringBuilderString으로 변환
public StringBuilder append(String str);
public StringBuilder insert(int offset, String str);
public StringBuilder replace(int start, int end, String str);
public StringBuilder delete(int start, int end);
public StringBuilder reverse();
public int capacity();
public void ensureCapacity(int minimumCapacity);
public int length();
public void setLength(int newLength);
public String toString();
public class Main {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder("Hello");

        // append
        sb.append(", World!");
        System.out.println(sb); // "Hello, World!"

        // insert
        sb.insert(6, "Beautiful ");
        System.out.println(sb); // "Hello Beautiful World!"

        // replace
        sb.replace(6, 15, "Amazing");
        System.out.println(sb); // "Hello Amazing World!"

        // delete
        sb.delete(5, 7);
        System.out.println(sb); // "HelloAmazing World!"

        // reverse
        sb.reverse();
        System.out.println(sb); // "!dlroW gnizamAolleH"

        // capacity and ensureCapacity
        sb.ensureCapacity(50);
        System.out.println("Capacity: " + sb.capacity());

        // setLength
        sb.setLength(10);
        System.out.println(sb); // "olleHgniza"

        // toString
        String result = sb.toString();
        System.out.println("Final String: " + result);
    }
}

5.3 StringBuilder의 내부 동작

  1. 내부 버퍼:
    • StringBuilder는 내부적으로 버퍼(기본 크기: 16)를 사용해 문자열을 저장
    • 문자열 길이가 버퍼 크기를 초과하면 버퍼 크기가 자동으로 확장
  2. 가변성:
    • 문자열 변경 시 기존 객체에서 직접 수정이 이루어지며, 새로운 객체를 생성하지 않음

언제 사용해야 할까?

  • 문자열 변경이 빈번할 때
    • 문자열 연결, 삽입, 삭제 작업이 많은 경우 String 대신 StringBuilder를 사용하면 성능이 향상
  • 멀티스레드 환경이 아닐 때
    • StringBuilder는 동기화가 없으므로 멀티스레드 환경에서는 StringBuffer를 고려

5.4 StringBuilder를 사용하지 않을 경우

  • 다음과 같은 코드는 성능에 영향을 미칠 수 있음
String result = "";
for (int i = 0; i < 100; i++) {
    result += i; // 매번 새로운 객체 생성
}
  • StringBuilder를 사용하면 효율적으로 작성할 수 있음
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++) {
    sb.append(i);
}
String result = sb.toString(); // 최종 문자열 생성

정리

  • StringBuilder가변 문자열 작업에 최적화된 클래스
  • append, insert, delete 등으로 문자열을 효율적으로 수정이 가능
  • 멀티스레드 환경에서는 동기화가 가능한 StringBuffer를 고려

6. StringBuffer

  • StringBufferJava의 가변 문자열 클래스로, 문자열을 동적으로 수정할 수 있도록 설계
  • StringBuffer는 멀티스레드 환경에서 안전(Thread-safe)하게 문자열을 처리할 수 있도록 내부 메서드들이 동기화(synchronized)되어 있음
  • 불변 객체인 String과 달리, 문자열을 변경할 때 새로운 객체를 생성하지 않고 기존 객체에서 작업이 수행

6.1 StringBuffer의 주요 특징

특징 String StringBuilder StringBuffer
가변성 불변 (Immutable) 가변 (Mutable) 가변 (Mutable)
수정 시 동작 새로운 객체 생성 같은 객체에서 변경 같은 객체에서 변경
멀티스레드 환경 안전하지 않음 안전하지 않음 안전 (Thread-safe)
성능 수정 작업이 많으면 비효율적 단일 스레드 환경에서 빠름 멀티스레드 환경에서는 느림
주요 사용 환경 변경이 적은 문자열 처리 단일 스레드에서 자주 변경되는 문자열 멀티스레드에서 자주 변경되는 문자열

6.2 StringBuffer의 주요 메서드

메서드 설명
append(String str) 문자열을 끝에 추가
insert(int offset, String str) 특정 위치에 문자열 삽입
replace(int start, int end, String str) 특정 범위를 다른 문자열로 대체
delete(int start, int end) 특정 범위의 문자열 삭제
reverse() 문자열을 뒤집음
capacity() 내부 버퍼의 크기 반환
ensureCapacity(int minimumCapacity) 내부 버퍼 크기를 최소값으로 확장
length() 현재 문자열의 길이 반환
setLength(int newLength) 문자열 길이를 설정
charAt(int index) 지정된 인덱스의 문자 반환
toString() StringBuffer 객체를 String으로 변환
public synchronized StringBuffer append(String str);
public synchronized StringBuffer insert(int offset, String str);
public synchronized StringBuffer replace(int start, int end, String str);
public synchronized StringBuffer delete(int start, int end);
public synchronized StringBuffer reverse();
public synchronized int capacity();
public synchronized void ensureCapacity(int minimumCapacity);
public synchronized int length();
public synchronized void setLength(int newLength);
public synchronized char charAt(int index);
public synchronized String toString();
public class Main {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("Hello");

        // append
        sb.append(", World!");
        System.out.println(sb); // "Hello, World!"

        // insert
        sb.insert(6, "Beautiful ");
        System.out.println(sb); // "Hello Beautiful World!"

        // replace
        sb.replace(6, 15, "Amazing");
        System.out.println(sb); // "Hello Amazing World!"

        // delete
        sb.delete(5, 7);
        System.out.println(sb); // "HelloAmazing World!"

        // reverse
        sb.reverse();
        System.out.println(sb); // "!dlroW gnizamAolleH"

        // capacity and ensureCapacity
        System.out.println("Capacity: " + sb.capacity()); // 현재 버퍼 크기
        sb.ensureCapacity(50);
        System.out.println("New Capacity: " + sb.capacity()); // 최소 50 이상

        // length
        System.out.println("Length: " + sb.length()); // 현재 문자열 길이

        // setLength
        sb.setLength(10);
        System.out.println(sb); // "olleHgniza"

        // charAt
        System.out.println("Char at 1: " + sb.charAt(1)); // 'l'

        // toString
        String result = sb.toString();
        System.out.println("Final String: " + result);
    }
}

6.3 StringBuffer의 내부 동작

  1. 내부 버퍼:
    • StringBuffer는 문자열을 저장하기 위해 내부 버퍼를 사용
    • 버퍼 크기가 문자열 길이를 초과하면 수정 작업이 버퍼 내에서 수행
    • 버퍼 크기를 초과하면 새로운 크기의 버퍼가 생성되며 기존 내용을 복사
  2. 동기화:
    • StringBuffer의 모든 메서드는 synchronized로 선언되어 있어, 멀티스레드 환경에서도 안전
    • 그러나 동기화로 인해 단일 스레드 환경에서는 StringBuilder보다 느림

6.4 StringBuffer vs StringBuilder

특징 StringBuffer StringBuilder
멀티스레드 환경 안전 (Thread-safe) 안전하지 않음
성능 동기화로 인해 느림 단일 스레드에서 더 빠름
사용 목적 멀티스레드 환경 단일 스레드 환경

언제 사용해야 할까?

  1. 멀티스레드 환경:
    • 여러 스레드가 문자열을 동시에 수정해야 할 때 StringBuffer를 사용
    • 동기화 덕분에 스레드 간 충돌을 방지
  2. 단일 스레드 환경에서는 비추천:
    • 단일 스레드 환경에서는 동기화가 필요 없으므로 성능이 더 빠른 StringBuilder를 사용하는 것이 좋음

정리

  • StringBuffer가변 문자열 처리를 위한 클래스
  • 멀티스레드 환경에서 안전하게 문자열 작업을 수행할 수 있도록 동기화가 적용
  • 문자열을 동적으로 수정해야 하는 멀티스레드 애플리케이션에서 유용
  • 단일 스레드 환경에서는 StringBuilder를 사용하는 것이 성능 면에서 더 적합

7. Arrays

  • Arrays 클래스는 배열을 조작하기 위한 다양한 유틸리티 메서드를 제공
  • 배열을 정렬, 검색, 복사, 비교, 변환 등의 작업을 효율적으로 수행

7.1 Arrays의 주요 메서드

메서드 설명
sort() 배열을 오름차순으로 정렬
binarySearch() 정렬된 배열에서 이진 검색으로 값을 검색
equals() 두 배열의 내용이 같은지 비교
fill() 배열을 지정된 값으로 초기화
copyOf() 배열을 복사하여 새 배열을 생성
copyOfRange() 배열의 지정된 범위를 복사
toString() 배열의 내용을 문자열로 변환
asList() 배열을 List로 변환
import java.util.Arrays;
import java.util.Comparator;

public class Main {
    public static void main(String[] args) {
        // 1. sort
        int[] numbers = {5, 2, 8, 3, 1};
        Arrays.sort(numbers);
        System.out.println("Sorted: " + Arrays.toString(numbers)); // [1, 2, 3, 5, 8]

        // 2. binarySearch
        int index = Arrays.binarySearch(numbers, 3);
        System.out.println("Index of 3: " + index); // 2

        // 3. equals
        int[] arr1 = {1, 2, 3};
        int[] arr2 = {1, 2, 3};
        System.out.println("Arrays equal: " + Arrays.equals(arr1, arr2)); // true

        // 4. fill
        int[] filledArray = new int[5];
        Arrays.fill(filledArray, 7);
        System.out.println("Filled: " + Arrays.toString(filledArray)); // [7, 7, 7, 7, 7]

        // 5. copyOf
        int[] copiedArray = Arrays.copyOf(numbers, 7);
        System.out.println("Copied: " + Arrays.toString(copiedArray)); // [1, 2, 3, 5, 8, 0, 0]

        // 6. toString
        System.out.println("Array as String: " + Arrays.toString(numbers)); // [1, 2, 3, 5, 8]

        // 7. asList
        String[] fruits = {"apple", "banana", "cherry"};
        System.out.println("List: " + Arrays.asList(fruits)); // [apple, banana, cherry]
    }
}

1. sort 메서드 (배열 정렬)

오름차순 정렬

int[] numbers = {5, 2, 8, 3, 1};
Arrays.sort(numbers);
System.out.println(Arrays.toString(numbers)); // [1, 2, 3, 5, 8]

부분 정렬

int[] numbers = {5, 2, 8, 3, 1};
Arrays.sort(numbers, 1, 4);
System.out.println(Arrays.toString(numbers)); // [5, 2, 3, 8, 1]

객체 배열 정렬

String[] words = {"banana", "apple", "cherry"};
Arrays.sort(words);
System.out.println(Arrays.toString(words)); // [apple, banana, cherry]

사용자 정의 정렬 (Comparator)

String[] words = {"banana", "apple", "cherry"};
Arrays.sort(words, Comparator.reverseOrder());
System.out.println(Arrays.toString(words)); // [cherry, banana, apple]

2. binarySearch 메서드 (이진 검색)

정렬된 배열에서 값 검색

int[] numbers = {1, 2, 3, 5, 8};
int index = Arrays.binarySearch(numbers, 5);
System.out.println(index); // 3 (5의 인덱스)

값이 없을 경우

int[] numbers = {1, 2, 3, 5, 8};
int index = Arrays.binarySearch(numbers, 4);
System.out.println(index); // -4 (찾는 값이 없음을 나타냄)

3. equals 메서드 (배열 비교)

int[] arr1 = {1, 2, 3};
int[] arr2 = {1, 2, 3};
int[] arr3 = {3, 2, 1};

System.out.println(Arrays.equals(arr1, arr2)); // true
System.out.println(Arrays.equals(arr1, arr3)); // false

4. fill 메서드 (배열 값 채우기)

int[] numbers = new int[5];
Arrays.fill(numbers, 7);
System.out.println(Arrays.toString(numbers)); // [7, 7, 7, 7, 7]

부분 채우기

int[] numbers = new int[5];
Arrays.fill(numbers, 1, 4, 9); // 1~3 인덱스에 9로 채움
System.out.println(Arrays.toString(numbers)); // [0, 9, 9, 9, 0]

5. copyOf 메서드 (배열 복사)

int[] numbers = {1, 2, 3};
int[] copy = Arrays.copyOf(numbers, 5); // 길이를 5로 확장
System.out.println(Arrays.toString(copy)); // [1, 2, 3, 0, 0]

6. copyOfRange 메서드 (부분 복사)

int[] numbers = {1, 2, 3, 4, 5};
int[] rangeCopy = Arrays.copyOfRange(numbers, 1, 4); // 1~3 인덱스 복사
System.out.println(Arrays.toString(rangeCopy)); // [2, 3, 4]

7. toString 메서드 (배열 출력)

int[] numbers = {1, 2, 3};
System.out.println(Arrays.toString(numbers)); // [1, 2, 3]

다차원 배열 출력

int[][] matrix = {{1, 2}, {3, 4}};
System.out.println(Arrays.deepToString(matrix)); // [[1, 2], [3, 4]]

8. asList 메서드 (배열을 리스트로 변환)

String[] fruits = {"apple", "banana", "cherry"};
List<String> list = Arrays.asList(fruits);
System.out.println(list); // [apple, banana, cherry]

주의: Arrays.asList로 생성된 리스트는 고정 크기입니다.

list.add("grape"); // UnsupportedOperationException 발생

9. parallelSort 메서드 (병렬 정렬 - Java 8 이상)

병렬 정렬 사용

int[] numbers = {5, 2, 8, 3, 1};
Arrays.parallelSort(numbers);
System.out.println(Arrays.toString(numbers)); // [1, 2, 3, 5, 8]

10. 기타 메서드

hashCode 메서드 (배열의 해시코드 생성)

int[] numbers = {1, 2, 3};
System.out.println(Arrays.hashCode(numbers)); // 배열의 해시코드 출력

deepHashCode 메서드 (다차원 배열의 해시코드 생성)

int[][] matrix = {{1, 2}, {3, 4}};
System.out.println(Arrays.deepHashCode(matrix)); // 다차원 배열의 해시코드 출력