개발/Swift

Swift에서 정렬: sort vs sorted

까칠코더 2025. 11. 11. 13:47
반응형

Swift에서 정렬: sort vs sorted 

 

Swift에서 배열을 정렬할 때 자주 혼동되는 두 메서드가 있습니다.

- sort(): 제자리(in‑place) 정렬 (자체 변경)
- sorted(): 새 배열을 반환 (원본 보존)


 

1. 핵심 요약

항목 sort sorted
반환값 없음 (Void) 새로 정렬된 [T] 반환
원본 변경 여부 변경됨 (in‑place) 변경되지 않음
메모리 사용 덜 함 (in‑place) 새 복사본 생성 → 더 큼
안정성 Swift 5+ 부터 안정 정렬(stable) 동일
사용 시점 임시 복사 불필요, 바로 수정할 때 원본 유지해야 할 때
가독성 명령형(sort()) 함수형(sorted())
일반적 시간복잡도 O(n log n) (TimSort 기반) O(n log n) (동일 알고리즘)

요약:
- 정렬된 배열이 필요하면 → sorted()
- 원본을 그대로 유지할 필요 없고, 제자리 정렬로 메모리 절약 → sort()


 

2. 기본 사용 예제

var numbers = [3, 1, 4, 1, 5]

numbers.sort()
print(numbers)          // [1, 1, 3, 4, 5]

let sortedCopy = numbers.sorted()
print(sortedCopy)       // [1, 1, 3, 4, 5]
print(numbers === sortedCopy) // false (서로 다른 배열)
  • sort()  배열 자체가 바뀜
  • sorted()  원본은 그대로 두고 복사본 반환

 

3. 커스텀 기준 정렬


3.1 오름차순 / 내림차순

let nums = [3, 1, 4, 1, 5]

let asc = nums.sorted(by: <)      // [1, 1, 3, 4, 5]
let desc = nums.sorted(by: >)     // [5, 4, 3, 1, 1]

3.2 클로저를 직접 전달

var people = ["Kim", "Lee", "Park"]
people.sort { $0.count < $1.count }   // 길이 순 정렬
print(people) // ["Kim", "Lee", "Park"]

클로저 { $0 < $1 }  비교 연산자 함수 < 와 동등합니다.


 

4. 안정 정렬(Stable Sort)

Swift 5 이후 sort()  sorted() 모두 stable sort 입니다.

즉, 동일 비교 결과의 요소는 원래 순서를 유지합니다.

struct Item { let id: Int; let priority: Int }
var items = [
    Item(id: 1, priority: 2),
    Item(id: 2, priority: 2),
    Item(id: 3, priority: 1)
]
items.sort { $0.priority < $1.priority }
print(items.map(\.id)) // [3, 1, 2] → id 1,2 의 상대 순서 유지됨

 

5. sort(by:)  sorted(by:) 차이 예제

var data = [3, 1, 4, 1, 5]

// 제자리(in-place) 정렬
data.sort(by: >)
print(data)   // [5, 4, 3, 1, 1]

// 원본 유지 + 정렬 결과 새로 반환
let newData = data.sorted(by: <)
print(data)   // [5, 4, 3, 1, 1]
print(newData) // [1, 1, 3, 4, 5]

sort(by:)  mutating 메서드 → 구조체(Array)의 값 자체가 변경됩니다.


 

6. 실무 패턴별 예제


6.1 정렬 결과를 UI/출력용으로만 쓸 때

let display = logs.sorted { $0.date > $1.date }
tableView.reload(with: display)

6.2 원본 배열 자체를 업데이트할 때

messages.sort { $0.timestamp > $1.timestamp }

6.3 문자열 길이, 대소문자 무시 정렬

let names = ["kim", "Park", "lee"]
let sortedInsensitive = names.sorted {
    $0.caseInsensitiveCompare($1) == .orderedAscending
}

6.4 Optional 속성 비교 (nil 마지막)

struct User { let name: String; let score: Int? }
var users = [
    User(name: "A", score: nil),
    User(name: "B", score: 80),
    User(name: "C", score: 50)
]

users.sort {
    switch ($0.score, $1.score) {
    case let (l?, r?): return l > r        // 둘 다 값 있음
    case (nil, _?):   return false         // nil 은 뒤로
    case (_?, nil):   return true
    default:          return false
    }
}

6.5 복수 기준 정렬 (다중 키)

let students = [
    ("Kim", 2), ("Lee", 1), ("Park", 2), ("Choi", 3)
]
let sortedStudents = students.sorted {
    if $0.1 == $1.1 { return $0.0 < $1.0 }   // 1순위 동점 → 이름순
    return $0.1 < $1.1                       // 1순위: 학년
}
print(sortedStudents)
// [("Lee",1),("Kim",2),("Park",2),("Choi",3)]

 

7. 성능 비교

방법 메모리 사용 원본 변경 복사 오버헤드 비고
sort() 낮음 O(1) 공간 없음 in-place
sorted() 높음 없음 새 배열 복사 functional
reversed() vs reverse() 동일 관계 (reverse() in-place) 동일 동일  

예제:

import Foundation

let data = (0..<1_000_00).map { _ in Int.random(in: 0..<1_000_00) }
var a = data

func measure(_ name: String, _ body: () -> Void) {
    let t1 = CFAbsoluteTimeGetCurrent(); body(); let t2 = CFAbsoluteTimeGetCurrent()
    print(name, ":", t2 - t1, "sec")
}

measure("sort") { a.sort() }
measure("sorted") { _ = data.sorted() }

일반적으로 sort()  약간 더 빠르고 메모리 효율적입니다.
하지만 둘 다 TimSort 기반으로 O(n log n) 이므로, 대부분의 경우 차이는 미미합니다.


 

8. 안정성(stability) 테스트 예제

struct Pair { let key: Int; let value: Int }
var pairs = [
    Pair(key: 1, value: 10),
    Pair(key: 2, value: 20),
    Pair(key: 1, value: 30)
]

pairs.sort { $0.key < $1.key }
print(pairs.map(\.value)) // [10, 30, 20] → 같은 key(1)의 순서 유지

 

9. 흔한 실수와 교정

실수 교정
let sorted = items.sort()  sorted가 Void let sorted = items.sorted()
sort() 후 원본 보존 기대 sorted() 사용해야 함
sort(by: <)  sorted() 혼동 sort는 mutating, sorted는 반환
대소문자 구분 안 한 문자열 정렬에서 < 사용 caseInsensitiveCompare 사용

 

10. 참조 링크


 

11. 결론

  • 원본 유지가 필요 → sorted() (함수형)
  • 원본 수정이 허용 → sort() (명령형, 메모리 효율적)
  • 복사 오버헤드 최소화가 목표면 sort()
  • 불변 데이터 흐름 / 함수형 스타일에는 sorted()
  • 둘 다 안정 정렬(TimSort 기반) 로, 결과 품질은 동일합니다.
반응형