본문 바로가기
이직&취업/Java 기초 상식

String, StringBuffer, StringBuilder 비교

by journeylabs 2025. 3. 14.
728x90
반응형

목차

    String, StringBuffer, StringBuilder 비교

    Java에서 문자열을 다룰 때, String, StringBuffer, StringBuilder 세 가지 주요 클래스를 사용할 수 있습니다. 

    자바 기본 상식 중의 하나로 꼭 숙지하시길 바랍니다. 

    각각의 특징과 차이점을 살펴보겠습니다.


    1. String (불변 객체, Immutable)

    String불변(Immutable) 객체입니다. 즉, 한 번 생성된 문자열은 변경할 수 없습니다.
    변경하려고 하면, 기존 문자열이 수정되는 것이 아니라 새로운 객체가 생성됩니다.

    📌 특징

    • 불변(Immutable) → 문자열이 변경될 때 새로운 객체가 생성됨
    • 리터럴(literal) 사용 가능 → "Hello"처럼 직접 할당 가능
    • String Pool 사용 → 동일한 문자열을 재사용하여 메모리 절약 가능
      ( Java에서는 String Pool(문자열 풀) 이라는 메모리 영역을 사용하여 동일한 문자열을 공유합니다.
      즉, 동일한 문자열 리터럴이 있으면 기존 객체를 재사용하여 메모리를 절약합니다.)
    • 성능 저하 가능성 → 문자열을 자주 수정하는 경우 새로운 객체가 반복적으로 생성되어 성능 저하 발생

    📌 예제

    public class StringExample {
        public static void main(String[] args) {
            String str1 = "Hello";
            String str2 = str1 + " World"; // 새로운 객체 생성
    
            System.out.println(str1); // "Hello"
            System.out.println(str2); // "Hello World"
            
            String str = "Hello";
            str = str + " World2";
    
            System.out.println(str); // "Hello World2"
        }
    }

     

     

    • 위 코드에서 str2 = str1 + " World"; 를 수행하면 기존 str1을 수정하는 것이 아니라, 새로운 String 객체가 생성됩니다
    • 두번째에서 "Hello World2"는 새로운 String 객체가 생성된 것입니다.
      이전의 "Hello" 객체는 더 이상 사용되지 않아 Garbage Collector (GC) 에 의해 제거됩니다.

    2. StringBuffer (가변 객체, Mutable, 스레드 안전)

    StringBuffer가변(Mutable) 객체로, 문자열을 변경할 때 새로운 객체를 생성하지 않고 기존 객체를 수정합니다. 또한 스레드 안전(Thread-safe) 하여 멀티스레드 환경에서도 안전하게 사용할 수 있습니다.

    📌 특징

    • 가변(Mutable) → 문자열을 변경해도 새로운 객체가 생성되지 않음
    • 성능 개선 → String보다 문자열 조작이 빈번할 때 유리
    • 스레드 안전(Thread-safe) → synchronized 키워드 사용으로 멀티스레드 환경에서 안전
    • 비교적 속도가 느림 → synchronized로 인해 동기화 비용이 발생 ( StringBuilder보다 느림.)

    📌 예제

    public class StringBufferExample {
        public static void main(String[] args) {
            StringBuffer sb = new StringBuffer("Hello");
            sb.append(" World"); // 기존 객체 변경
    
            System.out.println(sb); // "Hello World"
        }
    }

     

    • append()는 기존 문자열을 변경하므로 새로운 객체를 만들지 않습니다.
    • 메모리 사용량이 줄어들고 성능이 String보다 향상됩니다.
    public class StringBufferThreadExample {
        public static void main(String[] args) {
            StringBuffer sb = new StringBuffer("Hello");
    
            Runnable task = () -> {
                for (int i = 0; i < 5; i++) {
                    sb.append("!");
                    System.out.println(sb);
                }
            };
    
            Thread t1 = new Thread(task);
            Thread t2 = new Thread(task);
    
            t1.start();
            t2.start();
        }
    }

     

    • StringBuffer는 동기화(synchronized)가 적용되어 멀티스레드 환경에서도 안전하게 동작합니다.

    3. StringBuilder (가변 객체, Mutable, 스레드 비안전)

    StringBuilder는 StringBuffer와 동일한 가변(Mutable) 객체이지만, 스레드 안전(Thread-safe)이 보장되지 않습니다. 대신 동기화(synchronized)를 하지 않기 때문에 실행 속도가 더 빠릅니다.

    📌 특징

    • 가변(Mutable) → 문자열을 변경해도 새로운 객체가 생성되지 않음
    • 성능이 가장 빠름 → StringBuffer보다 동기화 비용이 없어 더 빠름
    • 스레드 안전하지 않음 → 멀티스레드 환경에서는 직접 동기화 필요
    • 싱글스레드 환경에서 유리

    📌 예제

    public class StringBuilderExample {
        public static void main(String[] args) {
            StringBuilder sb = new StringBuilder("Hello");
            sb.append(" World");
    
            System.out.println(sb); // "Hello World"
        }
    }
     

     

    • 멀티스레드 환경이 아니라면 StringBuffer 대신 StringBuilder를 사용하는 것이 더 좋습니다.
    • StringBuffer와 사용법이 동일하지만, synchronized가 없어서 더 빠름

    4. String vs StringBuffer vs StringBuilder 비교

    특성 String StringBuffer StringBuilder
    가변성 ❌ (불변, Immutable) ✅ (가변, Mutable) ✅ (가변, Mutable)
    메모리 관리 새 객체 생성 기존 객체 수정 기존 객체 수정
    성능 느림 (새 객체 생성) 중간 (동기화로 인해 약간 느림) 가장 빠름
    스레드 안전성 안전
    (불변 객체라 동기화 불필요)
    안전
    (synchronized 사용)
    안전하지 않음
    멀티스레드 환경
    추천 여부
    O O X

    5. 언제 사용해야 할까?

    • String: 문자열이 자주 변경되지 않고, 불변성을 유지해야 하는 경우 (String Pool을 활용하여 메모리 절약 가능)
    • StringBuffer: 멀티스레드 환경에서 문자열을 자주 변경해야 하는 경우
    • StringBuilder: 단일 스레드 환경에서 문자열을 자주 변경해야 하는 경우

    6. 성능 비교 (테스트 코드)

    아래 코드를 실행하면 String이 가장 느리고, StringBuilder가 가장 빠르다는 것을 확인할 수 있습니다.

    public class StringPerformanceTest {
        public static void main(String[] args) {
            long startTime, endTime;
            
            // String 테스트
            startTime = System.nanoTime();
            String str = "";
            for (int i = 0; i < 10000; i++) {
                str += "a";
            }
            endTime = System.nanoTime();
            System.out.println("String 수행 시간: " + (endTime - startTime) + " ns");
    
            // StringBuffer 테스트
            startTime = System.nanoTime();
            StringBuffer sbf = new StringBuffer();
            for (int i = 0; i < 10000; i++) {
                sbf.append("a");
            }
            endTime = System.nanoTime();
            System.out.println("StringBuffer 수행 시간: " + (endTime - startTime) + " ns");
    
            // StringBuilder 테스트
            startTime = System.nanoTime();
            StringBuilder sbd = new StringBuilder();
            for (int i = 0; i < 10000; i++) {
                sbd.append("a");
            }
            endTime = System.nanoTime();
            System.out.println("StringBuilder 수행 시간: " + (endTime - startTime) + " ns");
        }
    }
     

    결과 예시 (실제 실행 환경에 따라 다를 수 있음)

    String 수행 시간: 123456789 ns  
    StringBuffer 수행 시간: 56789 ns  
    StringBuilder 수행 시간: 34567 ns

     

    • String이 가장 느리고, StringBuffer는 synchronized로 인해 StringBuilder보다 조금 느림

    결론

    1. 문자열 변경이 적다면 String을 사용 (불변 객체로 메모리 절약 가능)
    2. 멀티스레드 환경에서 문자열 변경이 많다면 StringBuffer 사용 (스레드 안전)
    3. 싱글스레드 환경에서 문자열 변경이 많다면 StringBuilder 사용 (가장 빠름)

    이제 String, StringBuffer, StringBuilder의 차이점을 이해하고 상황에 맞게 선택해서 성능을 최적화하세요! 🚀

    728x90
    반응형