-
자바 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); // 마지막상태 : kjihgfedcbaString
- 자바에서 String 객체의 값을 변경할 수 없다.(불변)
- String이 불변인 이유
- 캐싱 : String pool에 각 리터럴 문자열의 하나만 저장하며 다시 사용하거나 캐싱에 이용가능하며 이로 인해 힙 공간을 절약
- 보안 : 데이터베이스 사용자 이름, 암호는 데이터베이스 연결을 수신하기 위해 문자열로 전달되는데, 만일 번지수의 문자열 값이 변경이 가능하다면 해커가 참조 값을 변경하여 애플리케이션에 보안 문제를 일으킬 수 있다.
- 동기화 : 여러 쓰레드에서 안정적으로 공유가 가능하다.
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)); // trueStringBuffer, 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로 통일하여 코딩하는것이 좋다.
'Java' 카테고리의 다른 글
자바 디자인 패턴(2)_팩토리 패턴 (0) 2023.11.06 자바 디자인 패턴(1)_싱글톤 패턴 (0) 2023.11.06 자바 new String()과 리터럴("")의 차이 (0) 2023.10.12 자바 URL 통신 사용법 및 총 정리 (0) 2023.10.12 자바 인터페이스와 추상클래스 차이점 (0) 2023.10.10