๐Ÿ“– String vs StringBuffer vs StringBuilder


์ž๋ฐ”์˜ ๋ถˆ๋ณ€ ๊ฐ์ฒด๋ฅผ ๊ณต๋ถ€ํ•˜๋‹ค๊ฐ€ String ๊ณผ StringBuilder๊ฐ€ ์˜ˆ์‹œ๋กœ ์ž์ฃผ ๋“ฑ์žฅํ•จ์„ ๋ณด๊ฒŒ ๋˜์—ˆ๋‹ค.
๋‚˜๋„ Java๋ฅผ ์ œ๋Œ€๋กœ ๊ณต๋ถ€ํ•œ์ง€ ์–ผ๋งˆ ์•ˆ๋˜์„œ,
์‚ฌ์‹ค String, StringBuffer, StringBuilder์˜ ๋ช…ํ™•ํ•œ ์ฐจ์ด๋ฅผ ์ž˜๋ชจ๋ฅด๊ณ 
๊ทธ๋ƒฅ ๊ฐ€์ ธ๋‹ค ์ผ๋Š”๋ฐ, ์ž๋ฐ”์˜ ์ •์„์„ ์ฝ์–ด๋ณด๋‹ˆ ์ด String์ด ๊ต‰์žฅํžˆ ํฅ๋ฏธ๋กœ์›Œ์„œ ๐Ÿ‘€
์ด๋ฒˆ ๊ธฐํšŒ์— ๋ช…ํ™•ํžˆ ์ •๋ฆฌํ•˜๊ณ  ๊ฐ€๋Š” ๊ฒƒ์ด ์ข‹๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

String

String์€ ์ฐธ ํŠน์ดํ•œ ์กด์žฌ์ด๋‹ค.
๋‹ค๋ฅธ ์–ธ์–ด์—์„œ ๋ฌธ์ž์—ด์ด๋ž€ ๋Œ€๋ถ€๋ถ„ charํ˜•์˜ ๋ฐฐ์—ด๋กœ ๋‹ค๋ฃจ๋Š”๋ฐ,
์ž๋ฐ”์—์„œ๋Š” ์ด ๋ฌธ์ž์—ด์„ ์œ„ํ•œ ํด๋ž˜์Šค๊ฐ€ ์กด์žฌํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

์ด String ํด๋ž˜์Šค๋ฅผ ๊นŠ๊ฒŒ ๋ณด๊ธฐ ์œ„ํ•ด String ํด๋ž˜์Šค๊ฐ€ ์–ด๋–ป๊ฒŒ ์ด๋ฃจ์–ด์ ธ ์žˆ๋Š”์ง€ ๋“ค์—ฌ๋‹ค๋ณด์ž.

String.java ์ผ๋ถ€

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0
    
    ...
    
    public String() {
        this.value = "".value;
    }

    /**
     * Initializes a newly created {@code String} object so that it represents
     * the same sequence of characters as the argument; in other words, the
     * newly created string is a copy of the argument string. Unless an
     * explicit copy of {@code original} is needed, use of this constructor is
     * unnecessary since Strings are immutable.
     *
     * @param  original
     *         A {@code String}
     */
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

    /**
     * Allocates a new {@code String} so that it represents the sequence of
     * characters currently contained in the character array argument. The
     * contents of the character array are copied; subsequent modification of
     * the character array does not affect the newly created string.
     *
     * @param  value
     *         The initial value of the string
     */
    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

String ํด๋ž˜์Šค๋Š” ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋กœ ๋ช‡ ๊ฐ€์ง€๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”๋ฐ
๊ทธ ์ค‘ ๋ฌธ์ž์—ด์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด ๋ฌธ์žํ˜• ๋ฐฐ์—ด ๋ณ€์ˆ˜ char[] value๊ฐ€ ์žˆ๋‹ค.
๋˜ ์ƒ์„ฑ์ž๋ฅผ ๋ณด๋ฉด ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ ์‹œ ์ƒ์„ฑ์ž์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ž…๋ ฅ ๋ฐ›๋Š” ๋ฌธ์ž์—ด์ด ์ด ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜์— ๋ฌธ์žํ˜• ๋ฐฐ์—ด๋กœ ์ €์žฅ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
๋˜ํ•œ final ํ‚ค์›Œ๋“œ๋กœ ๋ณ€๊ฒฝ์ด ๋ถˆ๊ฐ€๋Šฅํ•œ ๋ถˆ๋ณ€ ๊ฐ์ฒด์ž„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋˜ ์ƒ์„ฑ๋œ String ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋ฌธ์ž์—ด์€ ์ฝ์„ ์ˆ˜๋งŒ ์žˆ๊ณ , ๋ณ€๊ฒฝํ•  ์ˆ˜๋Š” ์—†๋‹ค.(๋ถˆ๋ณ€ ๊ฐ์ฒด)

๋งŒ์•ฝ ์—ฐ์‚ฐ์ž๋ฅผ ํ†ตํ•ด ๋ฌธ์ž์—ด์„ ๊ฒฐํ•ฉํ•˜๋Š” ๊ฒฝ์šฐ๋Š” String ์ธ์Šคํ„ด์Šค ๋‚ด์˜ value ๊ฐ’์ด ๋ณ€ํ•˜๋Š”๊ฒŒ ์•„๋‹Œ,
์ƒˆ๋กœ์šด ๋ฌธ์ž์—ด์ด ๋‹ด๊ธด String ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
๋•Œ๋ฌธ์— ๋ฌธ์ž์—ด์„ ๊ฒฐํ•ฉํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๋ฌธ์ž์—ด์„ ๊ฐ€์ง„ String ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
์ด๋Š” ๋ฌด๋ถ„๋ณ„ํ•œ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„ ์ฐจ์ง€์˜ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

String์˜ ๋น„๊ต

๋ฌธ์ž์—ด์„ ๋งŒ๋“ค ๋•Œ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์„ ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ String ํด๋ž˜์Šค์˜ ์ƒ์„ฑ์ž๋ฅผ ์ด์šฉํ•ด ๋งŒ๋“œ๋Š” ๋ฒ•์ด ์žˆ๋‹ค.

/*๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด*/
String str1 = "amazzi";
String str2 = "amazzi";

/*String ํด๋ž˜์Šค์˜ ์ƒ์„ฑ์ž*/
String str3 = new String("amazzi");
String str4 = new String("amazzi);

String ํด๋ž˜์Šค ์ƒ์„ฑ์ž

String ํด๋ž˜์Šค ์ƒ์„ฑ์ž๋Š” new ์—ฐ์‚ฐ์ž์— ์˜ํ•ด ์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ• ๋‹น๋œ๋‹ค.
wmr gkdtkd tofhdns String ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋˜๋ฉฐ ๊ฐ๊ฐ์˜ ์ฃผ์†Œ๊ฐ’์„ ๊ฐ–๋Š”๋‹ค.

๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด

๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์€ ํด๋ž˜์Šค๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์— ๋กœ๋“œ๋  ๋•Œ ์ž๋™์ ์œผ๋กœ ์ƒ์„ฑ๋˜์–ด ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๊ฒƒ์„ ์žฌ์‚ฌ์šฉํ•œ๋‹ค.
๋•Œ๋ฌธ์— ๊ฐ™์€ ๋‚ด์šฉ์˜ ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด์€ ํ•œ๋ฒˆ๋งŒ ์ €์žฅ๋˜๋ฉฐ
๊ฐ™์€ ๋ฌธ์ž์—ด์€ ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•˜๊ณ  ์ฐธ์กฐ๋ณ€์ˆ˜๋งŒ์ด ๋‹ค๋ฅผ ๋ฟ์ด๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด๋กœ ๋งŒ๋“  str1, str2์™€
String ํด๋ž˜์Šค์˜ ์ƒ์„ฑ์ž๋กœ ๋งŒ๋“  str3, str4 ๊ฐ๊ฐ์˜ ๋น„๊ต ๊ฒฐ๊ณผ๋Š” ์–ด๋– ํ• ๊นŒ?

str1.equals(str2); // true
str3.equals(str4); // true

str1 == str2; // true
str3 == str4 // false

equals() ์‚ฌ์šฉ์„ ํ†ตํ•œ ๋น„๊ต์—์„œ๋Š” ๋‘ ๋ฌธ์ž์—ด์˜ ๋‚ด์šฉ์„ ๋น„๊ตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‘ ๋น„๊ต ๊ฒฐ๊ณผ ๋ชจ๋‘ true์ด๋‹ค.
ํ•˜์ง€๋งŒ ๋“ฑ๊ฐ€๋น„๊ต์—ฐ์‚ฐ์ž ==๋ฅผ ํ†ตํ•ด ์ฃผ์†Œ๋ฅผ ๋น„๊ตํ–ˆ์„ ๊ฒฝ์šฐ,
str3, str4๋Š” ๊ฐ๊ฐ ๋‹ค๋ฅธ ์ฃผ์†Œ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— false๊ฐ€ ๋‚˜์˜จ๋‹ค.

StringBuffer

String ํด๋ž˜์Šค๋Š” ๋ถˆ๋ณ€ ํด๋ž˜์Šค์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ž์—ด์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์—ˆ์œผ๋‚˜,
StringBuffer๋Š” ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
StringBuffer๋Š” ํ• ๋‹น๋œ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋”๋ผ๋„,
String ํด๋ž˜์Šค์ฒ˜๋Ÿผ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ๊ธฐ์กด ํ• ๋‹น๋œ ๊ฐ’์„ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.
๋‚ด๋ถ€์ ์œผ๋กœ ๋ฌธ์ž์—ด ํŽธ์ง‘์„ ์œ„ํ•œ ๋ฒ„ํผ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ StringBuffer ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ๋•Œ
๊ทธ ํฌ๊ธฐ๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ StringBuffer ํด๋ž˜์Šค๋ฅผ ๋“ค์—ฌ๋‹ค๋ณด์ž.

StringBuffer.java ์ผ๋ถ€

public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{

    /**
     * A cache of the last value returned by toString. Cleared
     * whenever the StringBuffer is modified.
     */
    private transient char[] toStringCache;

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    static final long serialVersionUID = 3388685877147921107L;

    /**
     * Constructs a string buffer with no characters in it and an
     * initial capacity of 16 characters.
     */
    public StringBuffer() {
        super(16);
    }

    /**
     * Constructs a string buffer with no characters in it and
     * the specified initial capacity.
     *
     * @param      capacity  the initial capacity.
     * @exception  NegativeArraySizeException  if the {@code capacity}
     *               argument is less than {@code 0}.
     */
    public StringBuffer(int capacity) {
        super(capacity);
    }

    /**
     * Constructs a string buffer initialized to the contents of the
     * specified string. The initial capacity of the string buffer is
     * {@code 16} plus the length of the string argument.
     *
     * @param   str   the initial contents of the buffer.
     */
    public StringBuffer(String str) {
        super(str.length() + 16);
        append(str);
    }

    /**
     * Constructs a string buffer that contains the same characters
     * as the specified {@code CharSequence}. The initial capacity of
     * the string buffer is {@code 16} plus the length of the
     * {@code CharSequence} argument.
     * <p>
     * If the length of the specified {@code CharSequence} is
     * less than or equal to zero, then an empty buffer of capacity
     * {@code 16} is returned.
     *
     * @param      seq   the sequence to copy.
     * @since 1.5
     */
    public StringBuffer(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }

๋ฌธ์ž์—ด์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ toStringCache ๋ณ€์ˆ˜๊ฐ€ ๋ณด์ธ๋‹ค.
String ํด๋ž˜์Šค์™€ ์ฐจ์ด์ ์€ ์ด ๋ณ€์ˆ˜๋Š” final๋กœ ์„ ์–ธ๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.
์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ๋•Œ๋Š” StringBuffer(int capacity) ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ๊ธธ์ด๋ฅผ ์ง€์ •ํ•ด ์ค„ ์ˆ˜ ์žˆ์œผ๋‚˜,
์ง€์ •ํ•ด์ฃผ์ง€ ์•Š๋Š”๋‹ค๋ฉด 16๊ฐœ ๋ฌธ์ž๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ํฌ๊ธฐ์˜ ๋ฒ„ํผ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
๋งŒ์•ฝ ๋ฒ„ํผ์˜ ํฌ๊ธฐ๊ฐ€ ์ž‘์—…ํ•˜๋ ค๋Š” ๋ฌธ์ž์—ด์˜ ๊ธธ์ด๋ณด๋‹ค ์ž‘์„ ๋•Œ๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ๋ฒ„ํผ์˜ ํฌ๊ธฐ๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ์ž‘์—…์ด ์ˆ˜ํ–‰๋œ๋‹ค.

StringBuffer์˜ ๋ณ€๊ฒฝ

String ํด๋ž˜์Šค์™€๋Š” ๋‹ฌ๋ฆฌ append() ๋ฉ”์†Œ๋“œ๋กœ ๊ฐ’์„ ๋ณ€๊ฒฝ, ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.
append()๋Š” ๋ฐ˜ํ™˜ ๊ฐ’์ด StringBuffer์ด๋ฉฐ ์ž์‹ ์˜ ์ฃผ์†Œ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
๋•Œ๋ฌธ์— ์•„๋ž˜ ์ฝ”๋“œ๋Š” sb์— ์ƒˆ๋กœ์šด ๋ฌธ์ž์—ด์ด ์ถ”๊ฐ€๋˜๊ณ  sb ์ž์‹ ์˜ ์ฃผ์†Œ๋ฅผ ๋ฐ˜ํ™˜ํ•ด sb2์—๋„ sb์˜ ์ฃผ์†Œ๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค.

StringBuffer sb2 = sb.append("zzi");

StringBuffer์˜ ๋น„๊ต

String ํด๋ž˜์Šค์—์„œ๋Š” equals ๋ฉ”์„œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ํ•˜๊ณ  ์žˆ์–ด์„œ ๋‚ด์šฉ ๋น„๊ต๊ฐ€ ๊ฐ€๋Šฅํ–ˆ์œผ๋‚˜,
StringBuffer๋Š” ๊ทธ๋ ‡์ง€ ์•Š๋‹ค. ๋•Œ๋ฌธ์— ==๋กœ ๋น„๊ตํ•œ ๊ฒƒ๊ณผ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป๋Š”๋‹ค.

StringBuffer sb = new StringBuffer("abc");
StringVuffer sb2 = new StringVuffer("abc");
...
sb == sb2; // false
sb.equals(sb2); // false

ํ•˜์ง€๋งŒ toString()์„ ์˜ค๋ฒ„๋ผ์ด๋”ฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ์ด์šฉํ•ด ์ธ์Šคํ„ด์Šค๋ฅผ ์–ป๊ณ  equals()๋ฅผ ํ†ตํ•ด ๋น„๊ตํ•  ์ˆ˜ ์žˆ๋‹ค.

String s = sb.toString();
String s2 = sb2.toString();
...
s.equals(s2); // true

StringBuilder

StringBuffer๋Š” ๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋“œ์— ์•ˆ์ „ํ•˜๋„๋ก ๋™๊ธฐํ™”๋˜์–ด ์žˆ๋‹ค.
์ด์— ๋Œ€ํ•ด ์•„์ง์€ ์ž˜ ๋ชจ๋ฅด์ง€๋งŒ ๐Ÿฅฒ ์ผ๋‹จ ์ด ๋™๊ธฐํ™”๊ฐ€ StringBuffer์˜ ์„ฑ๋Šฅ์„ ๋–จ์–ด๋œจ๋ฆฐ๋‹ค.
๊ทธ๋ž˜์„œ StringBuffer์—์„œ ์Šค๋ ˆ๋“œ์˜ ๋™๊ธฐํ™”๋งŒ ๋บ€ ๊ฒƒ์ด StringBuilder์ด๊ณ 
๋‘˜์˜ ๊ธฐ๋Šฅ์€ ์™„์ „์ด ๋™์ผํ•˜๋‹ค.