포스트

String 클래스

String 클래스

String의 기본

1
2
3
4
5
6
7
8
9
10
public class CharArrayMain {

  public static void main(String[] args) {
    char[] charArr = new char[]{'h', 'e', 'l', 'l', 'o'}; // 옛날 방식
    System.out.println(charArr);

    String str = "hello"; // 자바에서 쉽게 사용하게 String 클래스를 제공
    System.out.println("str = " + str);
  }
}

참조형인데 어떻게 참조 값이 아닌 문자열을 넣을까?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class StringBasicMain {

  public static void main(String[] args) {

    String str1 = "hello";
    String str2 = new String("hello");
    String str3 = "hello";

    System.out.println(str1 == str2); // false
    System.out.println(str1.equals(str2)); // true
    System.out.println(str1 == str3); // true
    String intern = str2.intern();
    System.out.println(str1 == intern); // true
  }
}

리터럴 문자열의 경우 == 비교 시 true가 나오는데, 이는 풀에 이미 같은 값의 문자열이 존재하면 해당 참조 값을 가져오기 때문입니다.

new String() 한 경우라도 intern() 이라는 함수를 실행하면 같은 참조 값을 가집니다. 아마도 intern() 함수는 풀에 동일한 문자열이 있으면 해당 참조 값을 반환하는 것 같습니다.

String 비교

어떤 게 리터럴 문자열로 생성된 객체고 어떤 게 new String()으로 생성된 것인지 전부 알 수는 없습니다.

그래서 String 비교는 안전하게 equals() 함수를 통해 해야 합니다.

String이 불변 객체로 설계된 이유

같은 문자열의 경우 풀에서 같은 참조 값 찾아 사용한다고 했는데, 내부적으로 값이 수정이 가능해져 버리면 모두 수정되는 사이드 이펙트가 발생하게 되어 불변 객체로 설계된 것 입니다.

StringBuilder

불변인 String의 단점은 문자를 변경할 때 계속 객체를 만들고 GC를 해야 합니다. 따라서 자원을 더 많이 사용하게 됩니다.

StringBuilder는 가변 String으로 자원을 효율적으로 사용할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class StringBuilderMain1_1 {

  public static void main(String[] args) {
    StringBuilder sb = new StringBuilder();
    sb.append("A");
    sb.append("B");
    sb.append("C");
    sb.append("D");
    System.out.println("sb = " + sb);

    sb.insert(4, "Java");
    System.out.println("insert = " + sb);

    sb.delete(4, 8);
    System.out.println("delete = " + sb);

    sb.reverse();
    System.out.println("reverse = " + sb);

    //StringBuilder -> String
    String string = sb.toString();
    System.out.println("string = " + string);
  }
}

StringBuilder vs StringBuffer

  • StringBuilder: 동기화 오버헤드가 없어서 빠른 대신, 멀티 스레드 환경에서 안전하지 않음
  • StringBuffer: 내부에 동기화가 되어 있어 느린 대신, 멀티 스레드 환경에서 안전함

String 최적화

문자열 리터럴 최적화

1
2
String HelloWorld = "Hello, " + "World!"; // 컴파일 전
String helloWorld = "Hello, World!"; // 컴파일 후

런타임에 별도의 문자옆 결합 연산을 수행하지 않기 때문에 성능이 향상된다.

변수 최적화

1
2
String result = str1 + str2;
String result = new StringBuilder().append(str1).append(str2).toString(); // 최적화

자바 9부터는 StringConcatFactory 를 사용해서 자동으로 최적화를 수행합니다.

최적화가 어려운 경우

1
2
3
4
5
6
7
public static void main(String[] args) {
  long startTime = System.currentTimeMillis();
  String result = "";
  for (int i = 0; i < 100000; i++) {
    result += "Hello Java ";
  }
}
1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
  String result = "";
  for (int i = 0; i < 100000; i++) {
    result = new StringBuilder()
      .append(result)
      .append("Hello Java")
      .toString();
  }
}

이렇게 되기 이루어 지기 때문에 반복문에서는 비효율 적입니다.

1
2
3
4
5
6
7
8
public static void main(String[] args) {
  long startTime = System.currentTimeMillis();

  StringBuilder sb = new StringBuilder();
  for (int i = 0; i < 100000; i++) {
    sb.append("Hello Java ");
  }
}

반복문에서는 위와 같이 StringBuilder를 사용하여 줍시다.

반복문이 아닌 대부분의 경우 + 연산을 사용해도 최적화가 된다는 사실!

StringBuilder를 직접 사용하는 것이 더 좋은 경우

  • 반복문에서 반복해서 문자를 연결할 때
  • 조건문을 통해 동적으로 문자열을 조합할 때
  • 복잡한 문자열의 특정 부분을 변경해야 할 때
  • 매우 긴 대용량 문자열을 다룰 때

참고

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.