티스토리 뷰

스트링 클래스를 얘기할 때 흔히 불변성에 대해 언급한 것을 많이 본다. Immutable을 불변성으로 번역하는데, 이것이 무엇을 의미하는지 이해하기가 쉽지는 않다. 간단하게 말해서 스트링 객체는 한번 만들어지면 그 안에 포함된 문자열의 값이 바뀌지 않는다. 생성될 때 정해진 문자열 데이터가 변할 수 없다는 뜻이다. 

대표적인 예로 i번째 글자를 가져오는 charAt(i)라는 메소드는 있지만 i번째 글자를 수정할 수 있는 방법은 없다. 스트링에 들어있는 데이터를 일부라도 바꿀 수 있는 방법이 없다는 것이다. 그렇다면 + 연산이나 substring 메소드는 무엇인가? 일부를 바꾸는 replace 메소드는? 이런 메소드들은 스트링의 값을 변경시키는 것이 아니라 원래 객체는 그대로 두고 새로운 객체를 만들어서 돌려준다. 이것을 그림으로 나타내 보면 다음과 같다. 

그래서 스트링을 변경하는 연산을 많이 하면 자바의 가비지 콜렉션 시스템에서 성능에 영향을 줄 만큼 많은 쓰레기를 생성하게 된다. 예를 들어 파일에서 한 단어씩 읽어서 다음과 같이 스트링에 덧붙인다면 이것은 + 연산을 한번 할 때마다 새로운 스트링 객체를 하나씩 생성하고 이것들은 모두 쓰레기가 된다(즉 어떤 변수도 참고하지 않고 다시는 쓰이지 않을 객체가 됨). 

그렇다면 이런 질문이 있을 수 있다. 스트링을 불변이라고 하는 이유는 뭘까? 그렇게 문제가 된다면 불변이 아니게 해야 하지 않는가?

소프트웨어의 일반적인 특징으로 우리는 많은 스트링 값을 이용하게 된다. 예를 들어 학생의 이름, 학과, 성별 등이 문자열로 저장된다면 학과 스트링은 많은 학생들이 같은 값을 가리키게 된다. 그럴 때 같은 값을 가지는 스트링 객체의 참조 변수는 객체를 추가로 생성하지 않고 같은 객체를 참조하게 된다.  즉 백명의 학생이 있고 학과가 2개가 있다면 학과 이름(String) 객체는 2개만 존재한다. 성별(String) 객체도 2개만 생성되고 학생 객체들은 그것을 가리키게 된다. 스트링이 불변이기 때문에 이렇게 객체들이 공유하는 것이 가능하다. 예를 들어 어떤 학생 객체가 학과 이름을 replace 메소드로 변경한다면[각주:1] 그 학생의 학과명만 바뀌고(새로운 학과명의 스트링 객체가 생기고 그것을 가리킴) 그전 학과명을 가리키던 다른 학생들의 학과는 변함이 없다. 그러므로 여러 명의 학생이 같은 객체를 가리키고 있지만 그 중에 누군가가 값을 변경하면 그 학생만 학과가 바뀌고 다른 학생들은 그대로 기존의 학과명을 가리키면 된다.

만약 스트링이 불변이 아니라면 이러한 데이터 공유가 가능하지 않다. 어느 학생이든 자기가 가리키는 학과 객체명을 그 객체 내에서 바꿀 수 있어야 하므로 다른 학생에게 영향을 미치지 않으려면 모든 학생이 자신의 학과명 객체를 따로 가지고 있어야 한다. 그러면 우리는 같은 학과명 스트링 객체가 수백 개 생길 것을 예상할 수 있다.

이렇게 스트링 값의 특성 상 많은 곳에서 같은 값을 참조해야 하고 그것을 하나의 객체만 생성하여 해결하면 메모리를 많이 절약할 수 있다. 그러므로 자바에서는 스트링이 불변이라는 성질을 가지도록 해서 누구든 같은 값이면 같은 객체를 참조하게 한다. 그러나 값을 바꾸고 싶을 때는 변경한 값을 가지는 새로운 객체를 만들어서 가리켜야 한다.

그럼 스트링 객체를 공유하기 위해 선택한 스트링의 불변성 때문에 계산이나 변경 연산이 쓰레기를 많이 생성하게 되는 문제는 어떻게 해결해야 할까? 해결책은 지금부터 살펴볼 StringBuilder 클래스다. 이 클래스는 문자열 데이터를 덧붙이거나 바꾸거나 계산하여 만드는 과정에 자유롭게 사용할 수 있는 버퍼의 역할을 한다. C 언어에서의 문자 배열처럼 충분한 메모리를 가진 배열에 값을 계속 넣고 수정하고 붙이고 해서 문자열이 다 만들어지면 최종적으로 String으로 바꾼다. 

예를 들어 ArrayList에 있는 학생들의 이름을 모두 연결한 스트링을 돌려주는 다음과 같은 메소드를 생각해 보자.

String concatNames(ArrayList<Student> stList) {
    StringBuilder builder = new StringBuilder();
    for (Student s : stList) {
        builder.append(s.name + " ");
    return builder.toString();
}

StringBuilder 클래스는 new로 생성하면 내부적으로 버퍼 공간을 만들어서 그 안에서 append하는 데이터를 덧붙인다. 그리고 toString 메소드를 호출하면 그 내용을 스트링으로 만들어 반환한다. StringBuilder 클래스는 많은 메소드를 가지고 있으나 append와 toString을 주로 사용한다. 이에 대해서는 각자 따로 공부하기 바란다.

  1. 학과가 학과명을 바꾼 경우 컴퓨터과학과를 가리키던 모든 학생이 컴퓨터공학부를 가리키게 될 것이다. 이 때는 모든 학생의 학과 필드의 참조를 다 새로 생긴 객체로 바꾸어 주어야 한다. [본문으로]
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함