# Table of Contents
# Java 문자열 다루기
Java에서 문자열을 다루는 방법에 대해 정리한다.
# String Constant Pool
Java에서는 보통 다음과 같이 문자열 리터럴(String Literal)
로 문자열 타입의 변수를 초기화한다.
String str = "Hello";
문자열 리터럴은 힙 영역의 String Constant Pool
영역에 생성된다. 그리고 같은 문자열 리터럴은 한번만 생성된 후 같은 문자열을 공유한다.
String str1 = "Hello";
String str2 = "Hello";
String str3 = str1;
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // true
따라서 다음 세 변수 str1
, str2
, str3
는 같은 문자열 리터럴을 가리키게 된다.
# new String()
문자열은 String
클래스의 생성자로 초기화할 수도 있다.
String str = new String("Hello");
String
클래스의 생성자로 생성한 문자열은 일반 객체처럼 힙 영역에 저장된다.
생성자로 전달한 문자열은 value
이라는 char
타입 배열의 변수에 문자열을 저장한다. 이는 String
클래스의 정의에서 확인할 수 있다.
public final class String implements Serializable, Comparable<String>, CharSequence {
private final char value[];
}
이 때문에 문자열 리터럴
로 생성한 문자열 변수와 String
클래스의 생성자로 생성한 문자열 변수는 다른 주소값을 갖는다.
String str1 = "Hello";
String str2 = "Hello";
String str5 = new String("Hello");
System.out.println(str1 == str5); // false
System.out.println(str2 == str5); // false
String
클래스의 생성자를 사용하면 마치 일반 객체처럼 힙 영역에 매번 객체가 새롭게 생성된다.
String str5 = new String("Hello");
String str6 = new String("Hello");
System.out.println(str5 == str6); // false
# String.intern()
String.intern()
은 String Constant Pool
에서 리터럴 문자열이 이미 존재하는지 체크한 후 존재하면 해당 문자열을 반환하고 아니면 리터럴 문자열을 String Constant Pool
에 추가한다.
String str1 = "Hello";
String str2 = new String("Hello");
String str3 = str2.intern();
System.out.println(str1 == str2); // false
System.out.println(str1 == str3); // true
# 불변 객체
String Constant Pool
에 저장되는 문자열 리터럴은 불변객체다. 따라서 +
연산자 또는 String.concat()
로 문자열을 붙이는 작업을 수행하면 기존 인스턴스가 변경되는 것이 아니라 매번 새로운 인스턴스가 생성된다.
String str1 = "Hello";
String str2 = "World";
String str3 = "HelloWorld";
String str4 = str1 + str2;
String str5 = str1.concat(str2);
System.out.println(str3 == str4); // false
System.out.println(str3 == str5); // false
System.out.println(str4 == str5); // false
String str1 = new String("Hello");
String str2 = new String("World");
String str3 = new String("HelloWorld");
String str4 = str1 + str2;
String str5 = str1.concat(str2);
System.out.println(str3 == str4); // false
System.out.println(str3 == str5); // false
System.out.println(str4 == str5); // false
이처럼 문자열은 불변 객체이기 때문에 문자열 추가, 수정, 삭제 등의 연산이 빈번하면 힙 영역에 객체가 매번 새롭게 생성되어 성능에 영향을 주게 된다. 이러한 문제를 해결하기 위해 Java는 가변 객체인 StringBuffer
와 StringBuilder
를 도입했다.
# StringBuffer 클래스
StringBuffer
가 제공하는 append()
, insert()
, delete()
, replace()
등의 메소드를 제공하면 동일한 객체에서 문자열을 변경할 수 있다.
StringBuffer
예제를 살펴보자. StringBuffer
는 다음과 같이 생성한다.
StringBuffer buffer = new StringBuffer("Hello");
이제 뒤에 문자열을 붙여보자.
buffer.append(" world!");
위 코드는 아래처럼 객체를 새롭게 생성하는게 아니라
아래처럼 기존 객체의 값을 변경한다.
StringBuffer
클래스는 다양한 메소드를 지원한다.
# append()
끝에 문자열을 추가한다.
StringBuilder builder = new StringBuilder("abcd");
builder.append("efg");
System.out.println(builder); // abcdefg
# insert()
특정 위치에 문자열을 추가한다.
StringBuilder builder = new StringBuilder("1111");
builder.insert(2, 3333);
System.out.println(builder); // 11333311
# delete()
특정 범위의 문자열을 삭제한다.
StringBuilder builder = new StringBuilder("1122211");
builder.delete(2, 5);
System.out.println(builder); // 1111
# replace()
특정 범위의 문자열을 다른 문자열로 대치한다.
StringBuilder builder = new StringBuilder("1122211");
builder.replace(2, 5, "aaaa");
System.out.println(builder); // 11aaaa11
# reverse()
문자열을 뒤짚는다.
StringBuilder builder = new StringBuilder("123456");
builder.reverse();
System.out.println(builder); // 654321
# substring()
문자열의 일부분을 추출한다.
StringBuilder builder = new StringBuilder("123456");
String substring = builder.substring(0, 3);
System.out.println(substring); // 123
# toString()
StringBuilder
객체를 String
객체로 변환한다.
StringBuilder builder = new StringBuilder("123456");
String str = builder.toString();
System.out.println(str); // 123456
# StringBuilder 클래스
StringBuilder
는 StringBuffer
와 사용법이 거의 유사하다. 다만 다음과 같은 차이점이 있다.
StringBuffer | StringBuilder |
---|---|
동기화를 지원한다 | 동기화를 지원하지 않는다 |
멀티 스레드 환경에서 안전하다 | 멀티 스레드 환경에서 안전하지 않다. |
동기화 때문에 성능이 상대적으로 낮다. | 성능이 상대적으로 높다. |
따라서 단일 스레드 환경에서는 StringBuilder
를 사용하고 멀티 스레드 환경에서는 StringBuffer
를 사용한다. 요약하자면 문자열의 추가, 수정, 삭제가 빈번한 경우 String
보다 StringBuffer
또는 StringBuilder
클래스를 사용한다.
# 이스케이프 문자
Java에서 사용하는 이스케이프 문자(Escape Sequence)
는 다음과 같다.
문자 | 의미 |
---|---|
\' | 작은 따옴표 |
\" | 큰 따옴표 |
\n | New Line |
\t | Tab |
\\ | 역슬래시 |
\b | Backspace |
\f | Form Feed |
\r | Carriage Reture |
공백은 이스케이프 문자가 아니며 을 사용하면 된다.
# StringTokenizer
StringTokenizer
를 사용하면 문자열을 쉽게 자를 수 있다. 기본 구분자(delimiter)
는 \t\n\r\f
이다.
String str = "I am a programmer.\nhello world!";
StringTokenizer tokenizer = new StringTokenizer(str);
System.out.println(tokenizer.countTokens()); // 6
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
System.out.println(token);
}
I
am
a
programmer.
hello
world!
StringTokenizer
생성자의 두 번째 인자로 구분자를 직접 지정해줄 수 있다.
String str = "I am a programmer.hello world!";
StringTokenizer tokenizer = new StringTokenizer(str, ".");
System.out.println(tokenizer.countTokens()); // 6
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
System.out.println(token);
}
I am a programmer
hello world!
# String 클래스
String
클래스에는 문자열 조작을 위한 다양한 메소드가 존재한다.
# toUpperCase()
문자열을 대문자로 변환한다.
String name = "Leonel Messi";
System.out.println(name.toUpperCase()); // LEONEL MESSI
# toLowerCase()
문자열을 소문자로 변환한다.
String name = "Leonel Messi";
System.out.println(name.toLowerCase()); // leonel messi
# substring()
문자열의 일부분을 반환한다.
String name = "Leonel Messi";
System.out.println(name.substring(0, 6)); // Leonel
# trim()
문자열 앞과 뒤의 공백을 제거한다.
String name = " Hello World ";
System.out.println(name.trim()); // Hello World
# spilt()
인자로 전달받은 문자 또는 정규표현식을 기준으로 문자열을 나누어 배열에 저장하여 반환한다.
String str = "aa_bb_cc_dd_ee";
String[] spilt = str.split("_");
for (int i=0; i<spilt.length; i++) {
System.out.println(spilt[i]);
}
aa
bb
cc
dd
ee
다음과 같이 공백(Space)
을 기준으로 문자열을 자를 수도 있다.
String str = "aa bb cc dd ee";
String[] spilt = str.split(" ");
for (int i=0; i<spilt.length; i++) {
System.out.println(spilt[i]);
}
aa
bb
cc
dd
ee
이스케이프 문자를 기준으로 문자열을 자를 수도 있다.
String str = "Hello World!\nNice to meet you"; // Hello World! See you again
String[] spilt = str.split("\n");
for (int i=0; i<spilt.length; i++) {
System.out.println(spilt[i]);
}
Hello World!
See you again
OR(|)
연산자로 구분자 여러 개를 지정할 수도 있다.
String str = "a_b-c_d%e@f";
System.out.println(str);
String[] spilt = str.split("_|-|%|@");
for (int i=0; i<spilt.length; i++) {
System.out.println(spilt[i]);
}
a
b
c
d
e
f
# replace()
특정 문자를 다른 문자로 대치한다.
String str = "afafaf";
System.out.println(str.replace("a", "c")); // cfcfcf
# replaceAll()
특정 문자열을 다른 문자열로 대치한다.
String str = "afafaf";
System.out.println(str.replaceAll("af", "19")); // 191919
# startsWith()
문자열이 특정 문자열로 시작하는지 확인한다.
String str = "abcdef";
System.out.println(str.startsWith("ab")); // true
System.out.println(str.startsWith("aa")); // false
# endsWith()
문자열이 특정 문자열로 끝나는지 확인한다.
String str = "abcdef";
System.out.println(str.endsWith("def")); // true
System.out.println(str.endsWith("fff")); // false
# length()
문자열의 길이를 반환한다.
String str = "abcdef";
System.out.println(str.length()); // 6
# indexOf()
문자열에서 특정 문자의 인덱스를 반환한다. 특정 문자가 없으면 음수값을 반환한다.
String str = "abcde";
System.out.println(str.indexOf('b')); // 1
System.out.println(str.indexOf('z')); // -1
# charAt()
문자열에서 특정 인덱스에 위치하는 문자를 반환한다.
String str = "abcde";
System.out.println(str.charAt(0)); // a
System.out.println(str.charAt(1)); // b
System.out.println(str.charAt(2)); // c
# concat()
문자열 뒤에 추가한다. +
연산자와 동일하다.
String str = "aaa";
System.out.println(str.concat("ccc")); // aaaccc
System.out.println(str + "eee"); // aaaeee
# equals()
두 문자열이 동일한지 비교한다.
String str = "aaa";
System.out.println(str.equals("aaa")); // true
System.out.println(str.equals("ccc")); // false
# contains()
특정 문자나 문자열이 포함되는지 확인한다.
String str = "abcde";
System.out.println(str.contains("a")); // true
System.out.println(str.contains("bcd")); // true
System.out.println(str.contains("z")); // false
# compareTo()
문자열을 비교하는데 사용한다.
System.out.println("aaaa".compareTo("bbbb")); // -1
System.out.println("aaaa".compareTo("aaaa")); // 0
System.out.println("bbbb".compareTo("aaaa")); // 1
# matches()
문자열이 특정 패턴의 문자열을 포함하는지 확인한다. 특정 패턴은 정규 표현식을 의미한다.
String str = "aaacccaaa";
System.out.println(str.matches("(.*)ccc(.*)")); // true