java에서 빈 문자열을 비교할 때에는 어떤 메서드를 써야 할까요? length? 아니면 equals? 아니면 isempty? 이 셋이 어떻게 동작하는지 비교를 해 봅시다. 만약에 퍼포먼스가 차이가 난다면 어디서 차이가 나는지도 비교 분석해 보는 것도 좋은 공부가 될 듯싶습니다.

 

 


 먼저 이것을 테스트할 프로그램을 작성해 봅시다.

 

 

 random 객체를 생성한 다음에 무언가를 하고 있는데요. 문자열 하나를 만들 때마다, 길이도 랜덤 하게 정하고, 문자 또한 랜덤하게 정한다는 것을 알 수 있어요. 문자열을 만드는 작업은 StringBuilder 클래스를 이용하는데요. 단일 스레드 환경에서는 append 작업이 많이 일어나는 경우, String보다 성능이 좋고, StringBuffer보다 빠르기 때문입니다.

 

 String을 약 200만 개 만든 다음에 배열에다가 넣습니다. 그러면 테스트를 해야 할 겁니다. 어떻게 할까요?

 

 

 200만 개에 대해서, length()를 호출을 했습니다. 그리고 다른 하나는 equals를 호출했습니다.  39번째 println 문은 2개의 시간을 호출하는데요. 첫 번째 시간은 length로 검사했을 때의 시간, 2번째는 equals로 검사했을 때 시간을 나타낼 거예요.

 

 


 그러면 먼저 첫 번째 것을 봅시다.

 

 

 그냥 value의 length를 리턴합니다. value는 char형 배열인데요. 그냥 이것만 돌려줍니다.

 

 

 즉, 검사하고자 하는 문자열 객체, 그러니까 this의 value에 접근해서, 그것의 length만 가지고 옵니다. 조심할 점은, this가 null인지 검사를 하지 않는다는 것인데, 이 부분은 처리를 잘해 주셔야 합니다. 

 

 


 반면에 equals는 이야기가 조금 다릅니다. 이것은 단순히 길이만을 계산하는 게 아니라, 문자열의 내용이 같은지까지 비교를 합니다. 그러면 상대적으로, 길이만을 리턴하는 것보다는 복잡한 루틴이 들어갈 겁니다. 그리고, 특성상, 인자로 this와 인자로 넘어온 Object. 이 2개가 필요합니다. 이걸 가지고 무엇을 할까요?

 

 

 먼저 this랑 anObject를 ==으로 비교합니다. 같은 객체를 가리키고 있는지 비교를 하는데요. 일례로 String s1 = "abc";이고, String s2 = "abc";라면 s1과 s2는 같은 객체를 가리킵니다. 그런 경우를 먼저 검사를 합니다.

 

 

 

 만약에 모두 빈 문자열이고, 그것들이 모두 intern이 되었다면 이야기가 달라졌을 수도 있을 겁니다만, 현실적으로 빈 문자열만 들어오지는 않을 겁니다. "a"도 들어올 거고, "b"도 들어올 거고, "x"도 들어올 거고, "tam"도 들어올 거예요.

 

 심지어, s1과 s2가 가리키는 객체의 내용이 같지만, 객체 자체가 다른 경우도 있을 겁니다.

 

 

 이런 경우는? 977번째 if문에 안 걸릴 겁니다. 따라서, 980번째 줄부터 수행을 하게 될 거예요.

 

 

 그런데 instanceof가 있네요. 이것은 anObject가 String으로 형 변환이 가능한지를 검사하는 것인데요. Integer라던지, ArrayList는 불가능한가요? 그렇기 때문에 이 때에는, 980번째 줄에 if문에 걸리지 못해서 false가 리턴이 될 거고요. 그렇지 않고, String으로 변환이 가능하다면, if문 안쪽으로 들어갑니다. 이 연산자가 어떻게 퍼포먼스에 영향을 끼치는지는 추후에 다시 언급을 하도록 하겠습니다.

 

 그러면, 981번째 줄에서 anObject를 String형으로 강제 형 변환을 합니다. 편의상 anObject를 another라고 해 봅시다.

 

 

 982번, 983번째 줄을 보면 this.value.length와 another.value.length를 가지고 옮을 알 수 있어요. 두 객체의 필드에 접근을 하고 있어요. 만약에, 이렇게 길이가 다르다면, 983번째 if문 조건이 false가 되어서 그대로 false가 리턴이 될 거예요. 만약에 그렇지 않다면 이야기가 달라질 텐데요. 지역변수 v1, v2에 각각 this.value와 another.value의 객체 값을 넣습니다.

 

 그다음에 987번째 줄에 있는 while문에 들어올 건데요. 여기서 n의 값이 0이었으므로, while문이 수행되지 않고, 바로 992번째 줄로 넘어갑니다. true가 리턴되겠네요. 과정이 조금 복잡한데요. 가리키는 객체가 같은지 검사하고, String으로 변환이 될 수 있는지 instanceOf로 검사합니다. 그리고, 길이가 같은지를 검사하고, 같다면 안에 내용까지 검사를 하는데, 빈 문자열의 길이는 0이기 때문에, ""와 ""를 equals로 검사하는 경우에는, 바로 true가 리턴되는 셈입니다.

 

 만약에 길이가 1이나 2라면, 길이가 다르기 때문에 false가 리턴이 될 거예요. 정리하면 그렇습니다. length로 조건 판단을 하는 것보다는 복잡해 보입니다.

 

 


 보너스. isEmpty() 메서드는 어떨까요? 이것은 정말로 문자열이 비었는지 검사하는 메서드입니다.

 

 

 length 하고 다를 게 거의 없어 보입니다. 단지 다른 것은 value.length가 0인 경우에 true를, 아닌 경우 false를 리턴한다는 것입니다. int를 뱉느냐, 아니면 boolean을 뱉느냐의 차이일 뿐입니다. 이 세 메서드로 빈 문자열인지 비교를 하면, 퍼포먼스 차이가 어떻게 날까요?

 

 사실 isEmpty랑 length는 별 차이가 없습니다. null 포인터를 메서드 내에서 처리를 안 하는 것 또한 똑같습니다. 즉, 이 메서드를 호출하기 전에 null인지 확인을 해야 한다는 겁니다. equals랑 차이가 날 건데요.

 

 

 오른쪽이 빈 문자열을 equals로 검사했을 때 시간입니다. 길이가 [0,7]인 문자열들이 200만 개 있는 배열에서 equals로 비교했을 때, 0.12초가 걸렸다는 이야기입니다. 대략적으로 0.01 ~ 0.02초 차이가 나는데요. 무의미한 차이는 아닙니다. 10%에서 12% 정도의 퍼포먼스 차이는 무시하지 못합니다.

 

 기능만 봐도 알 수 있어요. length는 단순히 길이만 가져오면 됩니다. 하지만, equals는 문자열 자체가 같은지를 봅니다. 주 솟값이 같은지도 봐야 하지. 정말 String이랑 비교하고 있는지도 봐야 하지. 당연하게도 기능이 상대적으로 length보다 복잡할 수밖에 없을 겁니다. 빈 문자열은 길이가 0입니다. 그리고 길이가 0이면, empty string입니다. 이러한 점을 미루어 볼 때, 단순히 문자열이 비었는지 검사하기 위해서는 그냥 length나 isempty로 처리해도 된다는 것을 알 수 있습니다. 가독성을 위해서 isempty를 쓰는 게 좋아보이긴 하네요.