백준에서 문제를 풀 때 String을 만들면 메모리 초과를 겪을 수 있다. 또한 버퍼에 넣지 않고 그냥 출력했다가 시간 초과를 겪는 경우도 있다. 이 때 필요한 자료형이 StringBuffer나 StringBuilder다.
사용해보면 알 수 있는데, 둘은 사용법도 비슷하고 기능도 똑같다. (이름도 좀 비슷해보인다) 하지만 둘은 차이점이 분명히 존재한다! 오늘 둘의 사용법을 간단히 정리해보고, 차이점도 알아보자!
자바에서 문자열을 다루는 법
- 자바에서는 문자열을 다루는 자료형 클래스로 String, StringBuffer, StringBuilder 3가지를 지원한다.
- String은 불변(immutable)하는 자료형이다
- 한 번 생성된 String객체는 할당된 메모리 공간이 변하지 않는다.
- "abc"와 같은 String 리터럴은 Heap 메모리 영역(String contant pool)에 생성되며, 생성된 객체는 회수될 때까지 변하지 않는다
- String의 trim()이나 toUpperCase()를 사용해도 원본은 변하지 않고 완전히 새로운 String 객체가 반환된다
String s = "";
s += "a";
s += "b";
s += "c";
이 연산을 수행하면 "a"와 "ab", "abc" String 객체가 각각 만들어진다. 연산이 많아질 수록 메모리 낭비 뿐만 아니라 시간도 낭비된다(새로운 객체를 만들어야하기 때문)
자바에서 String을 불변으로 설정하면 다음과 같은 장점이 있다.
- 캐싱: String pool에 리터럴을 중복되지 않게 하나만 저장하여 재사용하여 힙 공간 절약이 가능하며, 이는 캐싱도 가능
- 보안: 데이터베이스 사용자 이름, 암호는 데이터베이스 연결을 수신하기 위해 문자열로 전달되는데, 번지수 문자열 값이 변경 가능하면 해커가 참조 값을 변경할 수 있다. 이는 애플리케이션에 보안 문제를 일으킬 수 있음
- 동기화: 실행되는 여러 스레드에서 안정적이게 공유 가능
StringBuffer 자료형
- 내부적으로 버퍼라는 독립적인 공간을 가짐
- 버퍼의 크기는 변할 수 있기 때문에 문자열 추가 시 공간이 낭비되지도 않고, 연산 속도도 매우 빠르다
- append로 문자를 추가해도 새로운 객체가 만들어지는 것이 아니라, 객체가 수정된다
- 문자열의 추가, 수정, 삭제가 빈번한 경우 String 대신 사용하면 좋다
사용법 간단하게 알아보기
StringBuffer sb = new StringBuffer(30); //초기 버퍼 크기 잡기
System.out.println(sb.capacity()); //버퍼 크기, 30
sb.append("abc"); //abc
sb.append("def"); //abcdef
sb.insert(0, "A"); //Abcdef
sb.delete(0, 4); //ef
- 초기 버퍼 크기는 16
- 문자열 연산 중 할당된 버퍼크기를 넘는다면 자동으로 버퍼 크기가 조절됨 (효율을 위해 넉넉하게 잡아주기)
- append로 여러 개를 추가해도 StringBuffer 객체는 3개가 만들어지지 않고 1개로 유지됨!
- insert나 delete로 문자열 수정도 가능
- subString, compareTo와 같이 String에서 제공하는 메서드들도 있다. 확인해보면 좋을 듯
StringBuffer와 StringBuilder
StringBuilder는 StringBuffer 클래스와 제공하는 메서드도 동일하고 사용법도 같다.
둘의 차이점은 딱 하나, 동기화 여부이다!
- StringBuffer: 멀티스레드 환경에서도 동기화를 지원한다 (각 메서드에 Synchronized가 붙어있음)
- StringBuilder는 동기화를 보장하지 않지만 StringBuffer보다 성능이 좋음 (동기화 처리를 해야하기 때문에 성능이 조금 떨어짐)
단순 조회연산이 많은 경우에는 String이 가장 빠르다. StringBuffer 등은 버퍼를 늘리고 줄이는 과정이 필요하기 때문에 String보다는 무겁다. 하지만 수정삭제가 빈번한 경우에는 StringBuilder나 StringBuffer를 사용하는 게 좋다!
멀티스레드 환경(web이나 소켓환경 등)일 때는 값 동기화 보장을 위해 StringBuffer를 사용해야하고, 단일스레드 환경이라면 StringBuilder를 사용하는 게 성능이 좋다.
백준과 같이 알고리즘 문제를 푸는 경우에는 StringBuilder를 사용하는게 좋겠고, 웹환경에서는 StringBuffer를 쓰는 게 안전하겠다.
더 알아보면 좋은 지식
//동일하게 동작함 - String객체가 계속 만들어지지 않고 StringBuilder 사용
String abc = "a" + "b" + "c";
String abc2 = new StringBuilder("a").append("b").append("c").toString();
//String 객체가 계속 만들어짐
String plus = "";
for (int i=0; i<10000; i++)
plus += "하";
- JDK 1.5버전 이전에서는 문자열 연산 시 조합된 문자열을 새로운 메모리에 할당하여 참조함 -> 성능상 이슈 발생
- JDK 1.5버전 이후에는 컴파일 단계에서 StringBulider로 컴파일되도록 변경됨
- (물론 반복루프로 계속 추가하면 객체를 계속 추가하게 된다)
요약
String에 계속 +연산을 수행하지말자! StringBuffer, StringBuilder라는 좋은 자료형을 사용하자!
StringBuffer는 멀티스레드 환경에서, StringBuilder는 조금 더 빠른 성능을 원할 때 사용한다.
백준에서는 StringBuilder쓰고, 웹 환경에서는 StringBuffer를 쓰자~
참고자료
'프로그래밍 언어 > Java' 카테고리의 다른 글
[JAVA] split vs StringTokenizer (1) | 2023.10.28 |
---|---|
[Java] 자바 코드 컨벤션 정리(Google Java Style Guide) (0) | 2023.10.18 |
[JAVA] Disjoint Set과 Union-Find 알고리즘(백준 1717 집합의 표현) (3) | 2023.10.10 |
[JAVA] 최소힙, 최대힙 사용법 정리(PriorityQueue) (0) | 2023.10.06 |
[JAVA] 이진탐색 lowerbound, upperbound 정리 (0) | 2023.10.01 |