ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 자바 String, StringBuffer, StringBuilder 차이
    Java 2023. 10. 12. 16:42

    자바 문자열 클래스

    • String
    • StringBuffer : 문자열을 연산(추가하거나 변경) 할 때 주로 사용하는 자료형 (멀티 스레드 환경에서 안전)
    • StringBuilder : 문자열을 연산(추가하거나 변경) 할 때 주로 사용하는 자료형 (문자열 파싱 성능이 가장 우수)
    /*String 내부구조 - final이 존재*/
    public final class String implements java.io.Serializable, Comparable {
    	private final byte[] value;
    }
    /*StringBuffer의 내부구조 - final이 미존재*/
    public final class StringBuffer implements java.io.Serializable {
    	private byte[] value;
    }
    
    
    String result = "";
    result += "hello";
    result += " ";
    result += "jump to java";
    System.out.println(result); // hello jump to java
    // → 심플하지만 연산 속도가 느리다는 단점이 있다
    
    
    /*
    StringBuffer 클래스는 내부적으로 버퍼(buffer)라고 하는 독립적인 공간을 가지게 되어, 
    문자열을 바로 추가할 수 있어 공간의 낭비도 없으며 문자열 연산 속도도 매우 빠르다는 특징이 있다.
    
    StringBuffer의 버퍼(데이터 공간) 크기의 기본값은 16개의 문자를 저장할 수 있는 크기
    문자열 연산중에 할당된 버퍼의 크기를 넘게 되면 자동으로 버퍼를 증강시킨다.
    */
    StringBuffer sb = new StringBuffer();  // StringBuffer 객체 sb 생성
    System.out.println(sb.capacity()); // 16 
    
    sb.append("hello");
    sb.append(" ");
    sb.append("jump to java");
    String result = sb.toString();
    System.out.println(result); // hello jump to java
    // → + 연산보다는 복잡해 보이지만 연산 속도가 빠르다는 장점이 있다

     

    • StringBuffer의 내장 메소드
    String str = "abcdefg";
    StringBuffer sb = new StringBuffer(str); // String -> StringBuffer
    
    System.out.println("처음 상태 : " + sb); // 처음상태 : abcdefg
    System.out.println("문자열 String 변환 : " + sb.toString()); // StringBuffer를 String으로 변환하기
    System.out.println("문자열 추출 : " + sb.substring(2,4)); // 문자열 추출하기
    System.out.println("문자열 추가 : " + sb.insert(2,"추가")); // 문자열 추가하기
    System.out.println("문자열 삭제 : " + sb.delete(2,4)); // 문자열 삭제하기
    System.out.println("문자열 연결 : " + sb.append("hijk")); // 문자열 붙이기
    System.out.println("문자열의 길이 : " + sb.length()); // 문자열의 길이구하기
    System.out.println("용량의 크기 : " + sb.capacity()); // 용량의 크기 구하기
    System.out.println("문자열 역순 변경 : " + sb.reverse()); // 문자열 뒤집기
    System.out.println("마지막 상태 : " + sb); // 마지막상태 : kjihgfedcba

     

    String

    • 자바에서 String 객체의 값을 변경할 수 없다.(불변)
    • String이 불변인 이유
      1. 캐싱 : String pool에 각 리터럴 문자열의 하나만 저장하며 다시 사용하거나 캐싱에 이용가능하며 이로 인해 힙 공간을 절약
      2. 보안 : 데이터베이스 사용자 이름, 암호는 데이터베이스 연결을 수신하기 위해 문자열로 전달되는데, 만일 번지수의 문자열 값이 변경이 가능하다면 해커가 참조 값을 변경하여 애플리케이션에 보안 문제를 일으킬 수 있다.
      3. 동기화 : 여러 쓰레드에서 안정적으로 공유가 가능하다.
    String str = "hello";
    str = str + " world";
    System.out.println(str); // hello world
    /*
    객체 자체를 업데이트 하는 것이 아니라,
    실제로 메모리에 새로 "Hello World" 값을 저장한 영역을 따로 만들고 변수 a 를 다시 참조하는 식으로 작동한다
    -> hello는 GC대상이 되고, hello world라는 새로운 영역을 참조
    */
    
    String sql = "abc";  // "abc"
    sql.toUpperCase();  // "ABC"
    System.out.println(sql); // "abc" - toUpperCase를 해도 자체 문자열은 변경되지 않는다 (불변)

     

    StringBuffer, StringBuilder

    • 문자열 데이터를 다룬다는 점에서 String 객체와 같지만, 객체의 공간이 부족해지는 경우 버퍼의 크기를 유연하게 늘려주어 가변(mutable)적이라는 차이점이 있다.
    • 내부 Buffer(데이터를 임시로 저장하는 메모리)에 문자열을 저장해두고 그 안에서 추가, 수정, 삭제 작업을 할 수 있도록 설계
      • .append() .delete() 등의 API를 이용하여 동일 객체내에서 문자열 크기를 변경하는 것이 가능
      • 문자열 수정,삭제가 많이 일어나면 String을 사용하는 것을 피해야한다. (String은 GC 대상이 계속 쌓이니까)

     

    String, StringBuffer, StringBuilder 동등 비교

    String str1 = "Hello"; // 문자열 리터럴을 이용한 방식
    String str3 = new String("Hello"); // new 연산자를 이용한 방식
    // 리터럴과 객체 문자열 비교
    System.out.println(str1 == str3); // false
    System.out.println(str3.equals(str1)); // true
    
    
    StringBuffer sb = new StringBuffer("hello");
    StringBuffer sb2 = new StringBuffer("hello");
    System.out.println(sb == sb2); // false
    System.out.println(sb2.equals(sb)); // false
    // StringBuffer객체를 toString()을 통해 String객체화를 하고 equals 비교
    String sb_tmp = sb.toString();
    String sb2_tmp = sb2.toString();
    System.out.println(sb_tmp.equals(sb2_tmp)); // true

     

    StringBuffer, StringBuilder 비교

    • 멀티 쓰레드 환경에서 안전하냐의 차이
    • StringBuffer : 쓰레드에서 안전하다. synchronized 키워드를 사용해 동기화 지원
    • StringBuilder : 쓰레드에서 안전하지 않다. 동기화를 지원하지 않음
    /* 
    두개의 멀티 쓰레드를 돌려 StringBuilder와 StringBuffer 객체에 
    각각 1 요소를 1만번 추가하는(append) 로직을 수행한 코드
    
    예상 : 한개의 쓰레드에서 배열요소를 1만번 추가하니 문자열 배열의 길이는 20000
    */
    
    import java.util.*;
    
    public class Main extends Thread{
      public static void main(String[] args) {
        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);
                
                /*
                StringBuffer는 멀티 쓰레드(multi thread)환경에서, 한 쓰레드가 append()를 수행하고 있을경우 
                다른 쓰레드가 append() 를 수행을 동시에 하지못하도록 잠시 대기를 시켜주고 순차적으로 실행
                -> 동시에 접근해 다른 값을 변경하지 못하도록 하므로 Thread Safe
                */
                // StringBuffer.length: 20000
                System.out.println("StringBuffer.length: "+ stringBuffer.length()); // thread safe 함
                
                /*
                쓰레드들이 동시에 StringBuilder 클래스에 접근해 동시에 append() 수행하다 
                몇번 씹혀서 제대로 수행이 안되어 일어난 결과
                */
                // StringBuilder.length: 19628
                System.out.println("StringBuilder.length: "+ stringBuilder.length()); // thread unsafe 함
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
      }
    }
    • web이나 소켓환경과 같이 비동기로 동작하는 경우가 많을 때는 StringBuffer를 사용하는 것이 안전하다
    • 자바 어플리케이션을 대부분 멀티 스레드 이상의 환경에서 돌아가기 때문에 왠만하면 안정적인 StringBuffer로 통일하여 코딩하는것이 좋다.

     
     
    출처)
    https://inpa.tistory.com/entry/JAVA-%E2%98%95-String-StringBuffer-StringBuilder-%EC%B0%A8%EC%9D%B4%EC%A0%90-%EC%84%B1%EB%8A%A5-%EB%B9%84%EA%B5%90

Designed by Tistory.