Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

보근은 참고 있다

String vs StringBuilder vs StringBuffer 본문

Language/자바

String vs StringBuilder vs StringBuffer

보근 2021. 7. 17. 21:31

 

 

 

 

 

 

 

 

String

자바에서 문자열을 나타내기 위한 클래스이다. 자바의 String은 특별한 취급을 받는다.

 

- 문자열 선언

String s1 = "abc";
String s2 = new String("abc");

s1 = "abcd";

자바의 참조 타입은 new 연산자를 통해 초기화하는 것이 원칙이지만, String은 위처럼 바로 문자열을 넣어 초기화할 수 있다.

 

String은 immutable하기 때문에 아래처럼 s1 객체에 "abcd"를 할당해주면 기존의 "abc"가 변하는 것이 아닌, 새로운 인스턴스 "abcd"를 만들어 할당해준다.

 

 

- 주소값 할당

String s = "a";

String s1 = "a";
String s2 = new String("a");
String s3 = String.valueOf('a');

System.out.println(s == s1);        // true
System.out.println(s == s2);        // false
System.out.println(s == s3);        // false

System.out.println(s.equals(s1));   // true
System.out.println(s.equals(s2));   // true
System.out.println(s.equals(s3));   // true

System.out.println(s == "a");                                     // true
System.out.println(new String("a") == new String("a"));           // false
System.out.println(String.valueOf('a') == String.valueOf('a'));   // false

자바에서 String을 선언하면 일단 메모리에서 해당 문자열이 있는지 찾아보고, 만약 있다면 해당 주소값을 참조하게 된다. 위의 코드로 예를 들어보면,

1. s를 "a"로 초기화할 때, 메모리에 "a"를 먼저 찾아본다. => 없기 때문에 새로 만들어 할당해준다. 

2. s1을 "a"로 초기화할 때, 메모리에 "a를 먼저 찾아본다. => 있기 때문에 그것을 참조한다.

3. s2는 new 연산자로 초기화 해주었다. => 새로운 인스턴스가 할당된다.

4. s3는 valueOf()로 인자를 String으로 캐스팅 해주었다. => 새로운 인스턴스가 할당된다.

 

위의 예시에서 보이듯이, new 연산자를 사용하거나, valueOf()를 사용하면 새로운 인스턴스가 만들어 지기 때문에 비교 연산자를 사용하면 서로 다른 것으로 취급한다.

 

 

- 더하기 연산

String s = "a";
s += "b";
s += 'c';
s += 1;

System.out.println(s);  //  abc1

자바는 String에도 더하기 연산을 사용할 수 있는데, 문자열을 합쳐준다. 또 문자열이 아닌 primitive 타입을 문자열과 더하기 연산을 하면, 문자열로 바꾸어 더해준다.

 

하지만, 문자열끼리 더하기 연산을 하면, 기존 문자열들은 메모리에 남아있고 새로운 문자열들이 메모리에 올라가게 된다. 위의 코드로 예를 들어보면,

1. "a"가 메모리에 올라간다.

2. "b"와 "ab"가 메모리에 올라간다.

3. "abc"가 메모리에 올라간다.

4. "abc1"이 메모리에 올라간다.

결국 "abc1" 이 하나의 문자열을 만들기 위해 쓰인 "a", "b", "ab", "abc", "abc1" 등이 메모리에 올라가게 되고 또, 새로운 인스턴스를 계속 만들어야 하기 때문에, 낭비가 발생하게 된다.

 

jdk9~11 중에서부터 String의 value가 char[]에서 byte[] 타입으로 바뀌어 오버헤드가 많이 줄었다고 하던데, 어쨌든 낭비는 낭비이다.

 

 

 

 

 

 

 

 

 

StringBuilder

문자열을 더 효율적이고 쉽게 관리하게 도와주는 클래스이다. StringBuilder는 String과 달리 mutable하다.

 

- append()

StringBuilder sb = new StringBuilder("a");

sb.append("b");
sb.append('c');
sb.append(1);

System.out.println(sb);           //  abc1
System.out.println(sb.toString);  //  abc1

String의 더하기 연산에서의 문제를 보완하기 위해 StringBuilder는 append() 메소드를 지원한다. String과 달리 append()메소드를 사용하면 기존의 문자열의 값을 변환하는 방식으로 동작하게 되어, 쓸데없는 문자열들이 메모리에 남지 않는다.

 

String s = "";
StringBuilder sb = new StringBuilder();

long start = System.currentTimeMillis();
for (int i = 0; i < 300000; i++) {
	s += i;
}

System.out.println(System.currentTimeMillis() - start);  // 63454

start = System.currentTimeMillis();
for(int i = 300000 ; i < 600000 ; i++) {
	sb.append(i);
}

System.out.println(System.currentTimeMillis() - start);  // 16

결과는 매번 가변적이겠지만, 매우 큰 처리량에서 차이가 확연히 보인다.

 

 

 

 

 

 

 

 

 

StringBuffer

StringBuffer는 synchronized가 붙어있어서 멀티 스레드 환경에서 쓰레드 세이프하게 동작한다. 

 

StringBuilder sb = new StringBuilder();
StringBuffer sf = new StringBuffer();

new Thread(() -> {
  for(int i = 0 ; i < 10000 ; i++) {
  sb.append(1);
  sf.append(1);
  }
}).start();

new Thread(() -> {
  for(int i = 0 ; i < 10000 ; i++) {
  sb.append(1);
  sf.append(1);
  }
}).start();

Thread.sleep(2000L);
System.out.println(sb.length());   // 18854
System.out.println(sf.length());   // 20000

StringBuilder는 스레드 세이프하지 못해 정신을 못 차리는 모습,

StringBuffer는 스레드 세이프하기 때문에 멀쩡한 모습을 볼 수 있다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'Language > 자바' 카테고리의 다른 글

자바의 Upcasting과 Downcasting  (0) 2021.07.18
Functional Interface  (0) 2020.11.01
Stream  (0) 2020.10.31
Optional  (0) 2020.10.31
SOLID (객체 지향 설계 원칙)  (0) 2020.10.28
Comments