본문 바로가기
연구하기/Computer Engineering

Java SE 에서 문자열 비교 이슈 정리

by 썰렁황제 2012. 8. 22.

  문자열은 문자의 집합이자, 거의 모든 경우에서 가변 크기를 가지는 데이터 타입인 만큼 많은 언어에서 포인터나 참조형을 사용한 형식으로 표현하고 있다. 물론 아닌 언어도 많다. 인터프리팅 방식의 언어들은 대다수 문자열을 Primative type 에 가까운 형태로 취급하고 있으니.

  뭐 일단 이에 대한 이야기는 여기서 꺼낼 것이 아니니 일단 뒤로 미루도록 하고, 오늘 볼 것은 Java SE 에서의 문자열 처리에 관한 것이다.


  자바의 문자열은 개체를 통해 표출하고 있고, 두 개체는 자기가 가진 멤버들의 값이 모두 같다고 하더라도 메모리상에서는 서로 다른 주소를 가지고 있기 때문에 비교 연산자 == 를 통해서는 두 개체가 같은 의미를 가지는 값인지 비교할 수는 없다. 따라서 보통 이를 위해 equals 함수를 오버라이딩 해서 사용하며, API 내에서 제공되는 클래스들은 많은 경우 이 함수들이 구현되어 있다. 여기에는 문자열도 해당된다.

  여기서 문자열 클래스 String 에 대해 잠시 고민할 필요가 있다. 자바의 String 클래스는, 최대한 값 형식에 가깝게 동작하게 하기 위해 String 자체가 가지는 값에 대해서는 불변(immutable)하도록 설정되어 있다. 그렇기 때문에 String 개체 내 문자의 일부를 치환한다거나 했을 때에도 해당 문자열을 직접 수정하는 것이 아니라 반드시 복사본을 만들도록 되어 있다. 하지만 단순히 = 등을 이용하여 할당하는 경우는 복사를 수행하는 것이 아니라 주소값만을 복사하게 된다.

  따라서 다음과 같은 경우 두 문자열은 == 연산으로도 같은 값을 가지게 될 것이다.

  Object a = new Object();

  String b = a.toString();

  String c = b;

  if (b == c) System.out.print("b is equal to c");

  다만 여기서 조금 색다른 사항이 존재한다.

  같은 문자열이지만 서로 다른 위치에서 선언된 두 문자열 상수를 참조한 String 객체는, 단순히 위의 개체 관점으로 본다면 == 비교로 같은 값이 나와서는 안될 것이다. 아래와 같은 경우 a와 b사이의 관계를 말한다.

String a = "Good!";

String b = "Good!";

boolean result = (a == b);

  위에서 result 의 값은? 개체 관점 비교로만 본다면 result = false 가 나와야 맞을 것이다. 하지만 실제로 수행해 보면 result = true 가 된다. 어 어째서???? 아 물론 단순히 값만 비교하면 같으니까 BASIC 등의 언어에 적응한 사람들은 오히려 이상한 점을 못느끼겠지만. (C++ 의 연산자 오버라이딩은 겉보기만 다르지 결과적으로는 equals 함수와는 별 차이가 없으니 그러려니 하자)


  아래 링크를 보자. 

  http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.5  


  아주 간단하게 정리하면, 이렇다.

  문자열 상수의 경우 컴파일 타임에서 문자열 풀(pool) 를 통하여 관리되어, 같은 문자열 상수이거나 결과적으로 같은 문자열 상수가 되는 것들 (예를 들면 "Hello!" 와 "Hel" + "lo!" 는 같은 주소를 가진다) 의 경우는 결과적으로 하나의 문자열 상수를 참조하도록 처리된다는 것이다. 그러니까 위에서 a 와 b 의 값에 따로따로 문자열 상수를 할당했지만, 실제로 컴파일을 수행하면 저 문자열 상수는 내부적으로는 한 군데에 저장된다는 거다.

  그러니 당연히 a 와 b 는 같은 값이 될 것이고, 이는 단순히 같은 문자열 상수 뿐만이 아니라 결과적으로 문자열 상수와 같은 결과를 만들어내는 상수 차원의 결과물도 마찬가지가 된다. (문자열 상수간 + 연산이라든가 static final 과 같은 상수 변수간의 연산 등)


  하지만 저것은 어디까지나 컴파일 시점의 문자열 풀에만 한정되는 이야기이므로, 런타임 중에 생성되는 문자열에 대해서는 아무런 의미를 가지지 못한다. 파일에서 읽어오든가, 또는 통신상에서 읽어오든가, 혹은 상수 변수가 아닌 상수 문자열을 참조한 지역변수와의 문자열 병합연산에 대한 결과물이라든가 등등은 해당되지 않는다. (위의 링크 참고)


  다만, 문자열 풀에 존재하는 문자열이라면 String 의 intern() 이라는 함수를 통하여, 현재 문자열과 같은 값을 가지는 문자열 풀의 문자열을 반환받을 수 있으며, 이를 통하여 == 연산을 하면 같다는 결과를 얻을 수 있다.


반응형