๋ฌธ์์ด์ ๋ค๋ฃจ๋ ์๋ฃํ์ ๋ํด์ ์์๋ณด๋๋ก ํ์.
๐ ๊ฐ๋ณ vs ๋ถ๋ณ
String์ ๋ถ๋ณ ๊ฐ์ฒด์ด๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก String์ ์๋ฐ์์ ๊ฐ์ ๋ณ๊ฒฝํ ์ ์๋ค.
์ฆ, ํ๋ฒ ์์ฑ๋๋ฉด ํ ๋น๋ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ด ๋ณํ์ง ์๋ ๋ถ๋ณ ๊ฐ์ฒด(Immutable)๋ผ๋ ๊ฒ์ด๋ค.
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
private final byte[] value;
}
์ค์ ์๋ฐ์์ String ๋ด๋ถ ๊ตฌํ ์ฝ๋๋ฅผ ๋ด๋ ์ ์ ์๋ค.
์ธ์คํด์ค ์์ฑ ์ ์์ฑ์์ ๋งค๊ฐ๋ณ์๋ก ์ ๋ ฅ๋ฐ๋ ๋ฌธ์์ด์ value๋ผ๋ ์ธ์คํด์ค ๋ณ์์ ๋ฌธ์์ด ๋ฐฐ์ด๋ก ์ ์ฅ๋๋ค.
value ๋ณ์๋ ์ฝ๋์์ ์ ์ ์๋ฏ์ด final. ์ฆ, ์์ํ์ผ๋ก ์ ์ธ๋์ด์์ผ๋ ๊ฐ์ ๋ฐ๊พธ์ง ๋ชปํ๋ค.
Java 9 ๋ถํฐ char[] ํ์ด ์๋ byte[] ํ์ผ๋ก ๋ฐ๋์๋ค.
๋ฌธ์์ด์ Latin-1(1byte)์ผ๋ก ํํ๋๋ ๊ฒฝ์ฐ์ ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ UTF-16(2byte)๋ก ๋๋์ด์ง๋ค.
์๋ฐ์ ๋ฌธ์์ด์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๋์ฝ๋๋ฅผ ์ง์ํ๊ธฐ ๋๋ฌธ์ ์์ด๋ฅผ ์ฐ๋ฉด 1byte๋ก ํํ์ด ๊ฐ๋ฅํ๋ค.
๊ทผ๋ฐ char[]ํ์ ๊ธฐ๋ณธ 2byte์ด๊ธฐ ๋๋ฌธ์, ์ธ๋ฐ์๋ ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น๊ฐ ์์๋ค.
์ด๋ฅผ char[](2byte) -> byte[](1byte) ๋ก ๋ฐ๊พธ๋ Compact Strings๋ผ๋ ์ต์
์ผ๋ก ๊ณต๊ฐ ํจ์จ์ ๋์๋ค.
String ๊ฐ ์กฐ์
String str = "hello";
str = str + " world";
System.out.println(str); // hello world
concat ๋ฉ์๋ ํน์ + ์ฐ์ฐ์๋ฅผ ํตํด ๊ธฐ์กด ๋ฌธ์์ด("hello")์ ์๋ก์ด ๋ฌธ์์ด(" world")์ ๋ถ์ธ๋ค๊ณ ๊ฐ์ ํ์.
์ด ์ฝ๋๋ ๋ด๋ถ์ ์ผ๋ก ์๋ก์ด String ๊ฐ์ฒด๋ฅผ ์์ฑํด์ ์๋ก์ด ๋ฌธ์์ด์ ์ ์ฅํ๊ณ , ์ด ์๋ก ์์ฑ๋ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ๋๋ก ๊ตฌํ๋๋ค.
์์ ๊ทธ๋ฆผ์ฒ๋ผ ์ฝ๋์์ผ๋ก๋ ๋ง์น ๋ฌธ์์ด์ ํธ์งํ ๋ฏ ๋ณด์ด์ง๋ง, ๋ด๋ถ ๋์์์ผ๋ก๋ ์ ํ ์๋๋ค.
์ฆ, ๋ฌธ์์ด์ ์กฐ์(์ถ๊ฐ,์์ ,์ญ์ )ํ๋ ์ผ์ด ๋ง์ผ๋ฉด Heap ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ญ๋นํ๊ฒ๋๊ณ , GC์ ์์ ์ ์ถ๊ฐ์์ผ ์ฑ์ ์ฑ๋ฅ์ ํ๋ฝ์ํค๋ ์ผ์ด ๋๋ค.
String์ด ๋ถ๋ณ์ด์ด์ ์ข์ ์ 3๊ฐ์ง
- ์บ์ฑ : String literal๋ก ์์ฑํ ๊ฐ์ฒด์ "๊ฐ"์ String pool์ด๋ผ๋ ๊ณณ์ผ๋ก ๋ค์ด๊ฐ๊ฒ ๋๋ค. (๊ฐ์ฒด๋ stack์์ญ์ ์กด์ฌ) String literal๋ก ์์ฑํ ๊ฐ์ฒด๊ฐ ์ด๋ฏธ String pool์ ์กด์ฌ ํ๋ค๋ฉด, ์ด ๊ฐ์ referenceํ๊ธฐ ๋๋ฌธ์ ํ ๋ฉ๋ชจ๋ฆฌ ์์ญ์ ์ ์ฝํ ์ ์๋ค. ๋ค๋ง, new ์ฐ์ฐ์๋ก ์์ฑํ๋ฉด ํ ๋ฉ๋ชจ๋ฆฌ์ ์ธ์คํด์ค๋ฅผ ๋ฌด์กฐ๊ฑด ์์ฑํ๊ฒ ๋๋ฏ๋ก ์ฃผ์ํ๋ค.
- ๋ณด์ : ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฌ์ฉ์ ์ด๋ฆ์ด๋ ์ํธ ๋ฑ์ ํ๋ผ๋ฏธํฐ๋ก ์ฃผ๊ณ ๋ฐ์ ๋ ๋ฌธ์์ด ๊ฐ์ด ๋ณ๊ฒฝ๊ฐ๋ฅํ๋ฉด ์ค๊ฐ์ ํด์ปค๊ฐ ์ฐธ์กฐ๊ฐ์ ๋ณ๊ฒฝํด์ ๋ณด์ ๋ฌธ์ ๋ฅผ ์ผ์ผํฌ ์ ์๋ค.
- ๋๊ธฐํ : ๋ถ๋ณํ๊ธฐ ๋๋ฌธ์ ๋์์ ์คํ๋๋ ์ฌ๋ฌ ์ค๋ ๋์์ ์์ ์ ์ด๊ฒ ๊ณต์ ๊ฐ ๊ฐ๋ฅํ๋ค. (Thread-Safe)
StringBuffer / StringBuilder๋ ๊ฐ๋ณ ๊ฐ์ฒด์ด๋ค.
StringBuffer์ StringBuilder์ ๊ณตํต์ ์ธ ๋ถ๋ถ์ ๋จผ์ ์์๋ณด๊ณ ์ฐจ์ด๋ ๋ค์ ์์๋ณด๋๋ก ํ๊ฒ ๋ค.
๋ณ๋์ ์ค๋ช ์ด ์๋ค๋ฉด ๊ณตํต์ ์ธ ๋ถ๋ถ์ด๋ค.
๋ฌธ์์ด ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃฌ๋ค๋ ์ ์์ String ๊ฐ์ฒด์ ๋น์ทํ์ง๋ง, ๋ด๋ถ์ ์ผ๋ก ๋ฒํผ๋ผ๋ ๋ ๋ฆฝ์ ์ธ ๊ณต๊ฐ์ ๋๊ณ ๊ฐ์ฒด์ ๊ณต๊ฐ์ด ๋ถ์กฑํด์ง๋ ๊ฒฝ์ฐ ๋ฒํผ์ ํฌ๊ธฐ๋ฅผ ์ ์ฐํ๊ฒ ๋๋ ค์ค๋ค. ์ฆ, ๊ฐ๋ณ(Mutable)์ ์ด๋ค.
public final class StringBuffer
extends AbstractStringBuilder
implements Serializable, Comparable<StringBuffer>, CharSequence
StringBuffer๋ AbstractStringBuilder๋ผ๋ ๊ฒ์ ์์ ๋ฐ๊ณ ์๋ค.
abstract class AbstractStringBuilder implements Appendable, CharSequence {
byte[] value;
byte coder;
}
์ฌ๊ธฐ์ ๋ณด๋ฉด final๋ก ์ ์ธ๋์ง ์์ ๊ฒ์ ๋ณผ ์ ์๋ค. -> ๊ฐ์ ๋ณ๊ฒฝํ ์ ์๋ค.
๋ด๋ถ์ ์ผ๋ก capacity(๋ฒํผ์ ํฌ๊ธฐ)๋ฅผ 16์ผ๋ก ๋๊ณ , ๊ณต๊ฐ์ด ๋ถ์กฑํ ๊ฒฝ์ฐ ์ด ๋ฒํผ์ ํฌ๊ธฐ๋ฅผ ๋๋ฆฐ๋ค.
StringBuffer stringBuffer = new StringBuffer();
System.out.println(stringBuffer.capacity()); // 16
stringBuffer.append("cat11111111111111");
System.out.println(stringBuffer.capacity()); // 34
StringBuffer / StringBuilder์ ๊ฐ ์กฐ์
StringBuffer sb= new StringBuffer("hello");
sb.append(" world");
StringBuffer์ StringBuilder๋ append()๋ delete()์ ๊ฐ์ ๋ฉ์๋๋ฅผ ํตํด ๋์ผ ๊ฐ์ฒด ๋ด์์ ๋ฌธ์์ด์ ๋ณ๊ฒฝํ๋ ๊ฒ์ด ๊ฐ๋ฅํ๋ค.
์์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ๋ฉ๋ชจ๋ฆฌ์ ๋ฐ๋ก ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ ๊ฒ์ด ์๋๋ผ ๊ทธ ์ธ์คํด์ค์ ๊ฐ์ ๋ณ๊ฒฝํ๊ธฐ ๋๋ฌธ์ ๋ฌธ์์ด์ ์กฐ์(์ถ๊ฐ, ์์ , ์ญ์ ) ์์ ์๋ก์ด ๊ฐ์ฒด๋ฅผ ๋งค๋ฒ ์์ฑํด๋ด๋ String๋ณด๋ค ๋น ๋ฅผ ์ ์๊ฒ ๋๋ ๊ฒ์ด๋ค.
๋ค๋ง, ์กฐ์ํ ํ์๊ฐ ์๋ ๋ชจ๋ String์ StringBuffer๋ก ์ฒ๋ฆฌํ๋ ๊ฒ์ ์ฑ๋ฅ์ ์ข์ง ์๋ค.
String์ ๋จ์ํ ์์๋ฌธ์์ด๋ง ๊ฐ๊ณ ์์ผ๋ฉด ๋๋ ๋ฐ๋ฉด์, StringBuffer/StringBuilder๋ ๋ฒํผ์ ํฌ๊ธฐ๋ฅผ ์กฐ์ํ๊ณ , ๋ค์ํ ์ถ๊ฐ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ ๋งํผ ๋ด๋ถ ๋์์ด ๋ ๋ณต์กํ๊ธฐ ๋๋ฌธ์ด๋ค.
๐ StringBuffer vs StringBuilder
StringBuffer์ StringBuilder๋ ์ ๊ณตํ๋ ๋ฉ์๋, ์ฌ์ฉ๋ฒ๋ ๋์ผํ๊ณ ํฌ๊ธฑ๊ฐ ์ ์ฐํ๊ฒ ๋ณํ๋ค๋ ๊ฐ๋ณ์ ํน์ง๋ ๋์ผํ๋ค.
์ด ๋์ ์ฐจ์ด๋ ๋๊ธฐํ์ ์ ๋ฌด์ด๋ค. ์ฆ, ๋ฉํฐ ์ฐ๋ ๋ ํ๊ฒฝ์์ ์์ ํ๋ ์๋๋๊ฐ ๊ทธ ๊ทผ๊ฐ์ด๋ค. ( Thread-Safe or Not )
StringBuffer๋ Thread-Safeํ๋ค.
// StringBuffer.append()
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
StringBuffer์ ๋ด๋ถ ์ฝ๋๋ฅผ ๋ณด๋ฉด ์๋ฐ์ synchronized ํค์๋๋ฅผ ์ด์ฉํด ๋ฉํฐ์ค๋ ๋์ ๋๊ธฐํ. ์ฆ, thread-safe๋ฅผ ๋ณด์ฅํ๋ค.
Java์์ synchronized ํค์๋๋ ์ฌ๋ฌ๊ฐ์ ์ค๋ ๋๊ฐ ํ ๊ฐ์ ์์์ ์ ๊ทผํ๋ ค๊ณ ํ ๋, ํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๊ณ ์๋ ์ค๋ ๋๋ฅผ ์ ์ธํ๊ณ ๋๋จธ์ง ์ค๋ ๋๋ค์ด ๋ฐ์ดํฐ์ ์ ๊ทผํ ์ ์๋๋ก ๋ง๋ ์ญํ ์ ํ๋ค.
StringBuilder๋ Thread Unsafe ํ๋ค.
// StringBuilder.append()
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
StringBuilder์ ๋ด๋ถ ์ฝ๋๋ฅผ ๋ณด๋ฉด ์ ์ ์๋ฏ์ด ๋๊ธฐํ ๊ด๋ จ ํค์๋๊ฐ ์๋ค.
์ด์ฒ๋ผ ์ฐ๋ ๋ ์์ ์ฑ์ ๋ณด์ฅํ์ง ์์ ๊ฒฝ์ฐ, [์ฐ๋ ๋1]์ด ๋ฌธ์์ด ๊ฐ์ ๋ณ๊ฒฝํ๋ ๋์ค์ [์ฐ๋ ๋2]๊ฐ ์ ๊ทผํ์ฌ ์ด์ํ ๊ฐ์ ๊ฐ์ ธ๊ฐ๋ค๋ ๊ฐ, ๋์์ ์ ๊ทผํด์ [์ฐ๋ ๋1]์ด ์์ ํ ๊ฐ์ ๋ฌด์ํ๊ณ ๊ฐ์ ๋ฎ์ด์์ฐ๋ ๋ฑ ์ฌ๊ฐํ ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์๋ค.
๋ฉํฐ ์ฐ๋ ๋ ํ๊ฒฝ์์ ์ฐ์ฐ ๊ฒฐ๊ณผ ํ์ธ
๋ ๊ฐ์ ์ฐ๋ ๋์์ ๊ฐ๊ฐ 1๋ง๋ฒ ๋ํ๋ ๋ก์ง์ ์ํํด๋ณด์.
StringBuffer stringBuffer = new StringBuffer();
StringBuilder stringBuilder = new StringBuilder();
new Thread(() -> {
for(int i=0; i<10000; i++) {
stringBuffer.append(1);
stringBuilder.append(1);
}
}).start();
new Thread(() -> {
for(int i=0; i<10000; i++) {
stringBuffer.append(1);
stringBuilder.append(1);
}
}).start();
new Thread(() -> {
try {
Thread.sleep(2000);
System.out.println("StringBuffer.length: "+ stringBuffer.length()); // thread safe ํจ
System.out.println("StringBuilder.length: "+ stringBuilder.length()); // thread unsafe ํจ
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
์ฐ๋ ๋ ์์ ์ฑ์ด ๋ณด์ฅ๋ StringBuffer๋ ์์๋๋ก 20000์ด ๋ ๊ฑธ ํ์ธํ ์ ์๋ค.
๋ฐ๋ฉด์ StringBuilder๋ ์ฝ 800๊ฐ์ ์ฐ์ฐ์ด ์ํ๋์ง ์์์ ํ์ธํ ์ ์๋ค.
๐ ์์ ์ฑ๋ฅ ๋น๊ต
String, StringBuffer, StringBuilder์ ์์ํ ์ฑ๋ฅ ๋น๊ต๋ฅผ ํด๋ณด๋๋ก ํ๊ฒ ๋ค.
50๋ง๋ฒ์ ๋ฃจํํด์ '*'๋ฌธ์์ด์ ์ถ๊ฐํ๋ ๋ก์ง์ ํ ์คํธ ํด๋ณด๊ฒ ๋ค.
final int lengths = 500000;
// ------------- (1) String์ +์ฐ์ฐ์ ์ด์ฉํด์ 500,000๊ฐ์ *๋ฅผ ์ด์ด ๋ถ์ธ๋ค.
long startTime1 = System.currentTimeMillis(); // ์์์๊ฐ์ ๊ธฐ๋ก (millisecond๋จ์)
String str="";
for(int i=0;i<lengths;i++){
str=str+"*";
}
long endTime1 = System.currentTimeMillis(); // ์ข
๋ฃ์๊ฐ์ ๊ธฐ๋ก(millisecond๋จ์)
// ------------- (2) StringBuffer๋ฅผ ์ด์ฉํด์ 500,000๊ฐ์ *๋ฅผ ์ด์ด๋ถ์ธ๋ค.
long startTime2 = System.currentTimeMillis();
StringBuffer sb = new StringBuffer();
for(int i=0;i<lengths;i++){
sb.append("*");
}
long endTime2 = System.currentTimeMillis();
// ------------- (3) StringBuilder๋ฅผ ์ด์ฉํด์ 500,000๊ฐ์ *๋ฅผ ์ด์ด๋ถ์ธ๋ค.
long startTime3 = System.currentTimeMillis();
StringBuilder sb2 = new StringBuilder();
for(int i=0;i<lengths;i++){
sb2.append("*");
}
long endTime3 = System.currentTimeMillis();
// ------------- ๋ฐฉ๋ฒ(1), ๋ฐฉ๋ฒ(2), ๋ฐฉ๋ฒ(3)๊ฐ ๊ฑธ๋ฆฐ ์๊ฐ์ ๋น๊ต
long duration1 = endTime1 - startTime1;
long duration2 = endTime2 - startTime2;
long duration3 = endTime3 - startTime3;
System.out.println("String์ +์ฐ์ฐ์ ์ด์ฉํ ๊ฒฝ์ฐ : "+ duration1); // 6309
System.out.println("StringBuffer์ append()์ ์ด์ฉํ ๊ฒฝ์ฐ : "+ duration2); // 9
System.out.println("StringBuilder์ append()์ ์ด์ฉํ ๊ฒฝ์ฐ : "+ duration3); // 4
String์ +์ฐ์ฐ์ ์ด์ฉํ ๊ฒฝ์ฐ : 6309
StringBuffer์ append()์ ์ด์ฉํ ๊ฒฝ์ฐ : 9
StringBuilder์ append()์ ์ด์ฉํ ๊ฒฝ์ฐ : 4
๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด StringBuilder๊ฐ ๊ธฐ๋ณธ ์ฑ๋ฅ์ ์ข๋ค๋ ๊ฒ์ ์ ์ ์๋ค.
StringBuffer๋ ๋๊ธฐํ๋ฅผ ์ํด ์ถ๊ฐ์ ์ธ ์์ ์ด ๋ค์ด๊ฐ๊ธฐ ๋๋ฌธ์ ์๋ฌด๋๋ ์๊ฐ์ด ๋ ๊ฑธ๋ฆฌ๋ ๊ฒ์ด๋ค.
์ด๋ฐ ์ด์ ๋ก String์์๋ ์ปดํ์ผ ์์ ์ '+'์ฐ์ฐ์ด StringBuilder๋ก ์๋ ์นํ๋๋ค.
Java 1.5 ์ดํ์ String ๋ง์ ์ฐ์ฐ์ ์ปดํ์ผ ์ ์ ๋ถ StringBuilder ์นํ๋๋ค๋ ์๊ธฐ๋ค.
String a = "hello" + "world";
/* ๋ ์๋์ ๊ฐ๋ค. */
String a = new StringBuilder("hello").append("world").toString();
// StringBuilder๋ฅผ ํตํด "hello" ๋ฌธ์์ด์ ์์ฑํ๊ณ "world"๋ฅผ ์ถ๊ฐํ๊ณ toString()์ ํตํด String ๊ฐ์ฒด๋ก ๋ณํํ์ฌ ๋ฐํ
์ฆ ์ด๋ฐ ์์ผ๋ก ๋ฐ๋๋ค๋ ๊ฒ์ด๋ค.
๋ฐ๋ผ์ ๋ง์ ๋ง์ ์ฐ์ฐ์ ํด์ผํ ๊ฒฝ์ฐ์ +๋ฅผ ๊ณ์ ์ฐ๋ ๊ฒฝ์ฐ, ์ด์ฐจํผ StringBuilder๋ก ๋ฐ๊ฟ์ฃผ๊ณ , ๊ฐ์ฒด ์์ฑํ๊ณ ๋ฑ๋ฑ ๋ถํ์ํ ์์ ์ด ์๊ธฐ๋ฏ๋ก, ์ ์ด์ StringBuilder๋ก ์์ฑํด์ append()ํ๋ ๊ฒ ๋ซ๋ค.
๐ ์ต์ข ์ ๋ฆฌ
String | StringBuffer | StringBuilder | |
์คํ ๋ฆฌ์ง ์์น | Constant String Pool | Heap | Heap |
๊ฐ๋ณ ์ฌ๋ถ | ๋ถ๋ณ | ๊ฐ๋ณ | ๊ฐ๋ณ |
์ฐ๋ ๋ ์์ ์ฑ | O | O | X |
์ฐ์ฐ ์๋ | ๋๋ฆผ | ๋น ๋ฅธ | ๋งค์ฐ ๋น ๋ฆ |
์ฌ์ฉ ์์ | ๋ฌธ์์ด ์ฐ์ฐ์ด ์ ๊ณ , ๋ฉํฐ์ค๋ ๋ ํ๊ฒฝ์ผ ๊ฒฝ์ฐ |
๋ฌธ์์ด ์กฐ์(์ถ๊ฐ,์์ ,์ญ์ )์ด ๋น๋ฒํ ๊ฒฝ์ฐ, ๋ฉํฐ ์ค๋ ๋ ํ๊ฒฝ์์ ์ค๋ ๋ ์ธ์ดํ๊ฐ ํ์ํ ๊ฒฝ์ฐ |
๋ฌธ์์ด ์กฐ์(์ถ๊ฐ,์์ ,์ญ์ )์ด ๋น๋ฒํ ๊ฒฝ์ฐ, ๋จ์ผ ์ฐ๋ ๋์ผ ๊ฒฝ์ฐ, ๋น ๋ฅธ ์ฐ์ฐ์ด ํ์ํ ๊ฒฝ์ฐ |
๐ Ref
https://starkying.tistory.com/entry/what-is-java-string-pool
https://medium.com/@vivekbansal1011/whats-synchronization-in-java-how-does-it-work-9a86db65f702
https://jiwondev.tistory.com/116