StringBuffer
예전에 알고리즘 문제를 풀다가 궁금한 게 생겨서 구글링했다가 베껴온 글인 것 같은데..
http://gall.dcinside.com/list.php?id=programming&no=110315&page=7
요약하면 어떤 네이버 블로그의 포스팅을 까는 건데 그 블로그는 요기.
http://blog.naver.com/damduck01/130025921363
대충 까는 요지는 두 가지이다.
1. 객체 변수를 null로 초기화 시켜놓고 오퍼레이션을 하려고 하는게 당연히 문제다.
2. setLength를 써서 초기화 하는 건 잘못된 정보다.
1번은 누가 봐도 문제다.
근데 블로그 주인장이 글을 나중에 고쳤는지는 모르겠지만 블로그에도 1번은 실수담으로 적어 놓은 것이며(본인도 문제가 된 이유는 포스팅 쓸 당시에는 잘 몰랐던 것 같지만) 그걸 정보라고 제공한다고 쓴 글이 아니다.
그러므로 글이 나중에 수정된게 아니라면 이건 까는 놈들의 국어실력의 문제이며 비판할 이유가 전혀 없다. 그러므로 이건 패스.
문제는 2번인데...
DC의 pupustory라는 아이디가 얼마나 대단한 개발자인 지는 모르겠지만 어쨌든 Java에 대한 지식은 어느 정도 있어 보이고 댓글의 내용을 종합해 보면, setLength는 저런 용도(사용 중이던 StringBuffer를 초기화 하는 용도)로 쓰는 것도 아니며, 저런 경우에는 new StringBuffer()로 해주는게 좋다고 써놨다. 다른 사람이 delete를 쓴다는 의견도 냈지만 이건 pupustory 말마따나 비효율적이고...
근데 남의 방법은 비효율적인 줄 알면서 본인의 방법은 비효율적인지 잘 모르는 듯...-_-;
이 경우에는 생각만큼 delete가 비효율적이진 않다. 오히려 pupustory의 방법이 제일 비효율적이다 -_-; 일단 이유는 아래에서...
StringBuffer
일단 StringBuffer란게 생긴 이유는 Java의 문자열이 수정 불가능한 객체이기 때문이다. 그러므로 StringBuffer이란 객체를 써서 내부에 char 배열을 갖고 문자열이 추가됨에 따라 이 배열을 확장시켜 가며 최종적으로 String으로 변환시킴으로써 중간의 문자열 추가에 대한 오버헤드를 줄이기 위한 목적으로 만들어졌다.
StringBuffer 객체가 생성되면 대략 아래와 같은 구조를 갖추게 된다.
{
char value[16];
int capacity; // 16
int length; // 0
}
특정 프로그래밍 코드가 아니니 그냥 가볍게 읽으시길.
간단하게 말하면 실제 데이터를 저장할 배열이 초기 길이 16으로 세팅되고 배열의 길이를 표시하는 capacity라는 변수가 있고(역시 16), 실제 데이터의 길이를 저장하는 length라는 변수가 있다(처음이므로 당연히 0).
그리고 append 등으로 문자열이 추가 될 때마다 length값이 늘어나게 된다.
그래서 16을 넘어가게 된다면? 배열 크기를 늘린다. 대략 두 배 크기로 늘리게 되므로 대충 아래처럼 바뀐다.
{
char value[32];
int capacity; // 32
int length; // 17
}
사용하는 동안 이런 식의 과정이 반복되며 늘 필요한 수준 이상의 char 배열을 확보한 채로 동작하게 된다.
delete, new StringBuffer(), setLength
그럼 delete를 하는 경우를 생각해 보자. delete 메써드의 기본 목적은 추가한 문자열 중 잘못된 부분을 삭제하는 것이다. 그렇기 때문에 중간의 문자열을 삭제하는 경우 그 뒷쪽의 문자열을 삭제한 부분에 복사해 채워넣는다. 여기까지는 pupustory의 말이 맞다.
하지만 이 경우는 전체 삭제이므로 그럴 바에는 new로 초기화 하겠다고 써놨는데 그야말로 하나만 알고 둘은 모르는 소리다.
전체 문자열에 대해 delete할 경우 뒤의 문자열을 카피하고 말고 할 것도 없으므로 length를 0으로 만들어 버리고 끝난다.
그러므로 delete를 주장한 놈은 내부적으로 setLength랑 똑같은 일을 하는 코드를 쓰면서 setLength 쓴 놈을 욕하는 꼴이고(제주 항공 타면서 진에어 무시하는 격), pupustory는 어레이 카피가 일어난다는 것만 생각하고는 delete도 살짝 무시하고 있는 것이다.
그럼 setLength(0)은 초기화의 의미가 있는가? 물론이다. 사용하는 데이터의 길이를 0으로 세팅하므로 배열의 길이 자체는 늘어나 있더라 할 지라도 논리적으로는 그냥 저장된 문자열이 없는 상태에서 처음부터 다시 쓰는 것이고 length 이후의 배열에 저장된 데이터를 가져다 쓸 일도 없으므로 기존에 무슨 데이터가 들어 있었는 지도 상관이 없다. 더더군다나 그 배열은 객체 외부에서 접근도 불가능하다. 이게 객체지향의 장점 아닌가?
pupustory라는 하나만 알고 떠드는 시키는 'setLength는 저렇게 쓰는게 아니지'란 식의 덧글을 달았던데 그럼 도대체 언제 쓴단 얘긴지?
Java API 문서에는 "setLength의 인자가 현재 length보다 크면 나머지 부분을 null 캐릭터로 채우며 작은 경우 length만 새로 바뀐다. 인자는 반드시 0 이상이어야 한다" 요약하면 이 정도로 써 있다. 못 쓸 이유가 전혀 없는데 도대체 어디서 줏어듣고 까부는 건지? -_-;;;
그럼 pupustory가 주장한 new StringBuffer()는?
적어도 초기화를 위해서 가장 확실해 보이기는 한다. 하지만 리소스를 재생성하는 것에는 언제나 오버헤드가 뒤따른다. 서버에서 쓰레드 풀이라든가 커넥션 풀같은 기능을 사용하는 이유는 새로 만드는 데에 그만큼 비용이 들기 때문에 커넥션 객체나 쓰레드를 새로 만들지 않고 재사용해서 새 것처럼 쓰는 것이다.
StringBuffer도 마찬가지다. 여러 번 자주 써야 하는 경우라면 setLength(0)을 사용하는 것이 객체 자체를 새로 만드는 것보다 아마 몇십배 이상 효율적일 것이다. 게다가 내부 배열도 어느 정도 크기를 갖고 있는 상태이므로 확장 비용도 들지 않는다. 배열이 차지하는 메모리가 아깝다면 역시 객체 재생성보다는 trimToSize() 메써드를 쓰는게 더 효율적이다.
그러므로 잘난 체 하던 인간이 주장하던 바는 실제로는 가장 비효율적인 방법이 된다.
본인이 주장하던 '인터넷에 잘못된 정보는 정말 무서운 거다'란 말을 되돌려주고 싶다.
Java API 같은 걸로 남을 까기 전에 최소한 소스나 문서 정도는 확인하자.
https://jeminency.tistory.com/172
StringBuffer의 setLength에 의한 초기화
참 오랜만에 글 쓴다. 게다가 뜬금없이 Java에 대한 포스팅... (스리슬쩍 어투도 반말로 전환..-_-;) 발단은 아래 글 때문이다. http://gall.dcinside.com/list.php?id=programming&no=110315&page=7 요약하면 어떤 네이
jeminency.tistory.com