Java & Android

Java String

그레이트쪼 2016. 9. 12. 19:29
  • Java에서 String은 immutable 객체로서 변경이 불가능하다. 


String concatenation - StringBuilder vs. StringBuffer

  • 따라서 String의 '+' 연산자를 통해 확장해나갈 경우 매순간 새로운 객체를 생성한다. 
1
2
3
4
5
6
7
8
// This code creates n String instances temporarily
public String repeatString (String str, int n) {
    String result = "";
    for (int i = 0; i < n; i++) {
        result += str;
    }
    return result;
}

  • 그에 반해 StringBuilder와 StringBuffer는 가변적인 크기의 공간 (자동으로 크기가 늘어나는 char [])을 가지기 때문에 append operation에서는 비용이 발생하지 않고 String을 최종 생성할 때만 객체 생성이 일어난다. 
1
2
3
4
5
6
7
8
// Just a String instance is created on calling toString()
public String repeatString (String str, int n) {
    StringBuffer buffer = new StringBuffer();
    for (int i = 0; i < n; i++) {
        buffer.append(str);
    }
    return buffer.toString();
}

  • StringBuffer와 StringBuilder는 operation이 동일하다.
  • 그렇다면 StringBuffer와 StringBuilder의 차이는 무엇인가? 가장 큰 차이는 StringBuffer가 synchronized되어 있고 (= thread safe) StringBuilder는 아니라는 점이다. (따라서 StringBuilder가 조금 성능이 더 좋을 것이다)
  • StringBuffer는 String과 함께 출시되었다. 반면 StringBuilder는 Java 5 (JDK 1.5)에서 새로 추가되었다. 
  • 성능 비교하자면 대충 이정도

 

  • 한편, Java 5 (JDK 1.5) 이상에서는 String의 "+" operation을 컴파일러가 자동으로 StringBuffer연산으로 바꿔준다.
1
2
3
4
// In Java 5, the following code
String s = "Hello " + "Beautiful " + "world";
// is changed like this automatically.
String s = (new StringBuffer ("Hello Beautiful ")).append("world").toString();
  • 그럼에도 불구하고 StringBuilder를 쓰자. StringBuffer보다 (성능상 조금) 나을 뿐 아니라, 컴파일러가 모든 상황에서 '+' 연산자를 다 알아서 변경해준다고 가정할 수는 없다. 예를 들어, 아래와 같은 코드는 변경 안해준다 (안해줄 것으로 예상된다).
1
2
3
4
// How about this code?
String s = "Hello ";
+= "Beautiful ";
+= "world";
  • Tip! - 마지막 iteration을 제외하고 뒤에 구분자 삽입하기
    • 보통은 iteration의 마지막을 체크하여 예외적으로 구분자를 삽입하지 않는 코드를 짠다.
1
2
3
4
5
6
7
8
9
10
int[] array = {123...};
StringBuilder sb = new StringBuilder();
 
for (int i : array) {
    if (sb.length() != 0) {
        sb.append(",");
    }
    sb.append(i);
}
return sb.toString();
    • 그런데 이를 달리 생각하여 첫 번째를 iteration을 제외하고 앞에 구분자 삽입하기로 바꿀 수도 있다.
1
2
3
4
5
6
7
8
9
int[] array = {123...};
StringBuilder sb = new StringBuilder();
 
sb.append(array[0]);
for (int i = 1; i < array.length; i++) {
    sb.append(",");
    sb.append(array[i]);
}
return sb.toString();



String decomposition - StringTokenizer vs. String.split()

  • StringTokenizer는 String을 기본적으로 공백문자 기준으로 token화 시키는 것이다.
  • 용법은 문자열을 생성자에 넣어주고 hasMoreTokens()와 nextToken()을 순환하면 된다. 
  • 예제 
1
2
3
4
5
StringTokenizer st = new StringTokenizer("this is a test");
 
while (st.hasMoreTokens()) {
    System.out.println(st.nextToken());
}

  • 공백문자가 아닌 다른 문자를 delimiter로 넣어주고 싶으면 생성자에 넣어주면된다. 예를 들어, 공백문자, 탭, 콤마를 delimiter로 지정하고 싶다면 아래와 같이 사용한다. (신기한 건 s와 t 앞에 backslash를 두 번 한다는 것이다. Regular expression에 근거)
1
StringTokenizer st = new StringTokenizer("this is a test"",\\s\\t");
  • 사실 StringTokenizer는 호환성을 위해 남겨진 코드이고 최근 코드에서는 String의 split()을 사용한다.
1
2
3
4
5
String[] tokens = "this is a test".split("\\s");
 
for (token : tokens) {
    System.out.println(token);
}

  • 기능적으로는 거의 같지만 약간의 차이는 있다. Tokenizer의 경우 delimiter가 연속되면 그 모든 것들이 무시되는 반면 split()의 경우에는 delimiter 사이를 공백문자열로 생성하여 배열을 반환한다.