개발을 시작하는 이야기

Swift의 문자열은 count보다 isEmpty를 사용해야 하는 이유 본문

개발 이야기/Swift

Swift의 문자열은 count보다 isEmpty를 사용해야 하는 이유

Teiresias 2022. 5. 16. 19:10

Swift 스터디에 참여해서 첫 자기소개 시간들을 갖게 되었는데 공유된 글에서 흥미로운 내용이 있어서 정리해둘겸 가져오게 되었다.


Swift에서 set, array, string을 사용하면서 값이 비어있는 경우를 추적할때 보통 다음과 같은 코드를 사용하곤 한다.

let string = ""

if string.count == 0 {
	print("There are no String")
}

string의 길이를 체크해서 0인 경우를 판단하는 코드로 위 코드만으로도 정상적으로 작동한다.

하지만 더 보기 좋고 깔끔하고 효율적인 코드가 있다. 

let string = ""

if string.isEmpty {
	print("string is Empty")
}

스위프트의 String 타입에서 count 대신 isEmpty를 사용하는것은 보기에도 좋지만 더 빠른 장점도 갖고 있다고 한다.

그렇다면 왜 더 빠른지에 대해 알아보면, Swift의 문자열이 어떤 방식으로 만들어지는지를 알아야 한다.

Swift의 문자열은 어떻게 만들어질까?

String 문자열은 Character 문자열들로 구성된 복잡한 컬랙션이다. 이러한 문자 기호들이 모여 하나의 심볼을 만들기도 한다.

 

예를들면, 지역을 표시자 "U"와 "S" 두개의 문자(리터럴 문자는 아니지만, 유니코드로 구성되어있는 문자)가 합쳐져서 미국을 상징하는 하나의 이모티콘이 되기도 한다. 각각의 문자로 나뉘어져 있는 경우에는 "U", "S"와 유사한 형태로 표시되지만, 이 둘이 나란히 합쳐지면 하나의 이모지 아이콘이 된다. 그러면서 문자열의 갯수를 확인하면 모두 1이 되어있다. str3은 str1과 str2의 합인 2개로 이루어져 있으나, 실제로는 그저 하나의 문자로만 보이게 된다.

위와 비슷한 케이스로, 유니코드 스칼라를 통한 "é"와 "⃝"이 합쳐지면서 "é⃝" (원 안에 들어간 é 형태의 문자인데 표시가 이렇게 되네)의 형태가 된다. 문자열 또한 1 + 1 = 1의 형태가 된다.

이렇듯 복잡한 문자들로 구성될 수 있는 컬렉션 형태의 String 문자열이기 때문에, 문자열 내의 문자 하나하나를 정수 값으로서 읽을 수가 없다.

위와 같은 컴파일 애러가 발생하게 된다. 왜냐하면 String 타입의 컬랙션은 기본적으로 배열과 같은 첨자접근이 불가능하기 때문이다. 대신 아래와 같은 방법으로 인덱싱을 할 수 있다.

 

하지만 위와 같은 구현은 단순하지 않다. 만일 문자의 5번째에 어떤 문자가 있는지 한번에 알 수 있는것이 아니기 때문에, 특정 문자열을 확인하기 위해서 앞의 문자열들을 하나씩 카운팅 해서 해당 문자열까지 도착해야 하기 때문이다. 즉, 문자열의 인덱싱 시간 복잡도는 일반적인 컬렉션 첨자접근시 시간복잡도인 O(1)이 아닌 O(N)이 되는 것이다. 이러한 특성을 이해하지 못한채 사용하게 된다면 문자열 내 각각의 문자들에 대한 작업을 할 때 많은 문제를 초래할수 있다.

위 코드를 간단히 생각하면 O(N)의 복잡도로 생각할 수 있다. 문자열의 문자를 한번씩 돌았다고 생각할수 있다. 그래서 첫번째 문자에서 1의 시간, 두번째 문자에서 1의 시간, N번째 문자에서는 N의 시간을 소요했고, 이는 곧 선형 시간을 갖게 된다고 말이다.

 

하지만, print(word[index]) 코드에서 사용된 첨자접근은 그 자체만으로 이미 O(N)의 시간 복잡도를 갖게 된다. 첨자접근을 할 때 해당 위치의 문자열을 얻기 위해 첫 인덱스부터 순차적으로 찾기 때문이다,. 그 결과, for 문의 각 연산당 N의 시간이 소요가 된다. 이는 O(N^2)의 시간 복잡도를 초래하게 된다. 위의 예시와 같이 사용하게 된다면 100초의 작업일 경우 10000초의 시간을 부담해야 하는 상황이 되는 것이다.

isEmpty

앞서 보았던것 처럼, Swift의 문자열에서 count를 사용할 경우 시간의 복잡도는 O(N)이 된다. Swift의 문자열은 문자 하나하나가 복잡한 구조의 값으로 되어있어 길이를 알기 위해서는 시작 인덱스로부터 한단계 한단계 카운트 하면서 계산하기 때문이다. 

 

이와 비교했을때 isEmpty는 단순히 한번의 간단한 체크를 통해 true, false를 반환할 수 있다. 단지 문자열의 첫 인덱스가 끝 인덱스와 같은지를 비교할 뿐이다. 이러한 연산은 단 한번의 연산으로 끝나며 시간의 복잡도는 O(1)이다. 이러한 점에서 isEmpty 연산은 count 연산과 큰 차이를 보이게 된다.