Swift 배열 vs 딕셔너리 성능 비교

Swift에서는 자료구조로 배열(Array)딕셔너리(Dictionary)가 자주 사용됩니다.
이 두 자료 구조는 각각 특정 상황에 적합하지만, 삽입, 삭제, 검색, 정렬 등의 작업에서 성능 차이가 발생합니다.
이번 포스팅에서는 배열과 딕셔너리의 성능 차이를 코드로 비교하고, 각각의 특성과 사용 시 주의할 점을 알아보겠습니다.

1. 배열(Array)과 딕셔너리(Dictionary)의 기본 개념

배열 (Array)

배열은 순차적 데이터 구조로, 데이터를 연속된 메모리 공간에 저장합니다.
배열은 인덱스를 통해 데이터에 빠르게 접근할 수 있는 장점이 있으며, 이는 O(1)의 시간 복잡도를 가집니다.
하지만 중간에 데이터를 삽입하거나 삭제할 때는 O(n)의 시간이 걸립니다.

딕셔너리 (Dictionary)

딕셔너리는 키-값 쌍으로 데이터를 저장하는 구조입니다.
딕셔너리는 해시 테이블을 기반으로 하여, 키를 통해 데이터에 빠르게 접근할 수 있습니다.
키를 통한 검색, 삽입, 삭제는 O(1)의 시간 복잡도를 가지지만, 해시 충돌이 발생하면 성능 저하가 있을 수 있습니다.
또 딕셔너리는 순서를 보장하지 않기 때문에 정렬이 필요한 경우에는 별도의 처리가 필요합니다.

2. 배열과 딕셔너리의 성능 차이 비교

2.1. 삽입 및 출력 성능 비교 코드

import Foundation

struct People {
    let age: Int
    let name: String
}

@main
struct Main {
    static func main() {
        let largeDataSize = 1000000
        
        // 딕셔너리에 미리 데이터를 삽입
        var dict: [String: People] = [:]
        for i in 0..<largeDataSize {
            let person = People(age: i % 100, name: "Person \(i)")
            dict["key\(i)"] = person
        }
        
        // 배열에 미리 데이터를 삽입
        var array: [People] = []
        for i in 0..<largeDataSize {
            let person = People(age: i % 100, name: "Person \(i)")
            array.append(person)
        }
        
        // 딕셔너리 출력 성능 테스트
        let startDictTime = CFAbsoluteTimeGetCurrent()
        
        for (key, value) in dict {
            _ = "\(key): \(value)" // 출력 대신 성능 테스트를 위한 변수로 대체
        }
        
        let endDictTime = CFAbsoluteTimeGetCurrent()
        let dictElapsedTime = endDictTime - startDictTime
        print("딕셔너리 출력 시간: \(dictElapsedTime)초")
        
        
        // 배열 출력 성능 테스트
        let startArrayTime = CFAbsoluteTimeGetCurrent()
        
        for person in array {
            _ = "\(person)" // 출력 대신 성능 테스트를 위한 변수로 대체
        }
        
        let endArrayTime = CFAbsoluteTimeGetCurrent()
        let arrayElapsedTime = endArrayTime - startArrayTime
        print("배열 출력 시간: \(arrayElapsedTime)초")
    }
}

2.2. 성능 테스트 결과

위 코드를 실행한 결과는 상황에 따라 다를 수 있지만, 일반적으로 배열이 딕셔너리보다 출력 성능에서 더 빠를 수 있습니다. 아래는 예시 결과입니다.

딕셔너리 출력 시간: 3.104477047920227초
배열 출력 시간: 3.0223690271377563초

배열이 딕셔너리보다 출력 성능에서 빠른 이유는, 배열이 연속된 메모리 공간을 사용하여 순차적인 메모리 접근이 가능하기 때문입니다. 반면, 딕셔너리는 해시 테이블 기반으로 메모리에서 흩어져 저장되기 때문에, 메모리 접근 효율이 떨어집니다.


3. 삽입, 삭제, 검색 성능 비교

3.1. 삽입 성능

3.2. 삭제 성능

3.3. 검색 성능

3.4. 성능 비교 요약 표

작업 배열 성능 딕셔너리 성능
삽입 O(1) (끝에 삽입)
O(n) (중간 삽입)
O(1)
삭제 O(1) (끝에서 삭제)
O(n) (중간 삭제)
O(1)
검색 O(1) (인덱스 기반)
O(n) (값 기반)
O(1) (키 기반)
출력 O(n) O(n)

4. 정렬이 필요한 경우

정렬이 필요한 경우에는 배열이 딕셔너리보다 더 적합합니다. 배열은 내장된 정렬 메서드를 사용하여 쉽게 데이터를 정렬할 수 있으며, 정렬 후에도 인덱스 기반 접근이 가능하기 때문에 정렬과 검색 작업을 동시에 수행할 수 있습니다.

var numbers = [5, 3, 8, 1, 9]
numbers.sort()  // 배열을 오름차순으로 정렬
print(numbers)  // 출력: [1, 3, 5, 8, 9]

반면, 딕셔너리는 순서를 보장하지 않기 때문에, 정렬된 상태를 유지하기 어렵습니다. 딕셔너리에서 정렬된 데이터를 출력하려면, 키나 값을 배열로 변환한 후 정렬해야 합니다.


5. 결론

배열과 딕셔너리는 각각 장단점이 뚜렷한 자료구조입니다. 상황에 따라 적절한 자료구조를 선택하는 것이 중요합니다.

모든 데이터를 한 번에 출력해야 하거나, 순차적인 데이터를 처리해야 하는 경우 배열이 더 효율적일 수 있으며, 특정 키를 사용하여 빠르게 데이터를 찾아야 하는 경우 딕셔너리가 더 유리합니다.


이제 배열과 딕셔너리의 성능 차이를 이해하고, 각각의 상황에 맞는 자료구조를 선택하는 데 도움이 되었기를 바랍니다!