├── .gitignore ├── Images └── big-o.png ├── LICENSE ├── README.md └── helloworld.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /Images/big-o.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/younatics/Collection-Data-Structures-Swift-KR/b14082890471d3b13d7833542477646fc750fa63/Images/big-o.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Seungyoun Yi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Collection Data Structures in Swift - KR 2 | Swift의 Collection Data Structures를 소개합니다. 좋은 글이라 생각되어 번역합니다. 핵심적인 부분을 제외하고는 번역을 제외하고 의역했습니다. 본문은 [여기](https://www.raywenderlich.com/123100/collection-data-structures-swift-2)를 참고해주세요. 3 | 4 | ## Introduction 5 | 많은 데이터를 처리하는 어플리케이션을 만들고 있을 때를 생각해보세요. 그 데이터를 어디에 넣을 건가요? 어떻게 그 많은 데이터를 효율적으로 다룰 건가요? 6 | 이럴 때는 `Array`, `Dictionary`, `Set`과 같은 기본적인 데이터 구조들이 필요합니다. 이 `collection data structures`에 명확한 개념을 가질 경우 처리 속도는 어마어마하게 빨라 질 것입니다. 7 | ##### 튜토리얼의 순서는 다음과 같습니다. 8 | 1. 데이터 구조가 무엇인지 배우고, `Big-O` 표기법에 대해 배워봅니다. 9 | 2. 각 데이터 구조의 퍼포먼스를 확인해봅니다. 10 | 3. 기존의 Cocoa 데이터 구조와 Swift의 새로운 데이터 구조의 퍼포먼스를 비교해봅니다. 11 | 4. 추가의 팁으로 다른 구조들을 조금 더 알아봅니다. 12 | 13 | ## What is `Big-O` Notation? 14 | 15 | 컴퓨터에서 데이터를 처리할 때 걸리는 * 시간 * 또는 차지하는 * 메모리 공간 * 은 일반적으로 * 데이터의 양 * 에 비례하게 됩니다. 16 | Big-O 표기법은 이때 필요한 시간, 공간을 * 점근적으로(asymptotically) * 표현하는 방법이며, 이렇게 표현한 것을 시간복잡도(time complexity), 공간복잡도(space complexity)라고 부릅니다. 17 | 18 | 아래는 자주 만날 수 있는 시간복잡도의 Big-O 표기법과 설명을 빠른 것부터 나열한 것입니다. 19 | - `O(1)`: 제일 이상적입니다. 처리하는 데이터의 양(n)에 관계없이 상수 시간만큼 소요되는 경우입니다. 20 | - `O(log n)`: 좋은 퍼포먼스를 보여주는 그래프입니다. 데이터 구조에 있는 아이템이 늘어나도 매우 천천히 연산의 횟수가 늘어나게 됩니다. (걸리는 시간도 마찬가지) 21 | - `O(n)`: 중간 단계의 퍼포먼스를 보여주는 그래프입니다. 데이터 구조랑 연산횟수가 일차함수(linear)를 그리며 증가합니다. 22 | - `O(n (log n))`: (데이터가 10M~100M 넘어갈 경우) 낮은 단계의 퍼포먼스를 보여주는 그래프입니다. 비교 기반 정렬(comparison based sort)의 시간복잡도 하한(lower bound)이기도 합니다. 23 | - `O(n²) `: 데이터 구조에 있는 아이템 수의 제곱에 비례하여 늘어납니다. 여기서부턴 성능이 매우 떨어지고, 추천하지 않습니다. 24 | - `O(2^n)`: 2의 데이터 구조의 개수 제곱만큼 증가합니다. 매우 나쁜 퍼포먼스를 보여줍니다. 원소 n개 집합의 모든 부분집합을 출력하는 경우의 시간복잡도와 같습니다. 25 | - `O(n!)`: 최악의 케이스입니다. 이렇게 코딩이 될 경우에는 답이 없습니다. 26 | 27 | ![Big-o](Images/big-o.png) 28 | 29 | ## Common iOS Data Structures 30 | `Array`, `Dictionary`, `Set`이 가장 흔한 데이터 구조입니다. 각각의 구조와 성능에 대해 알아봅니다. 또한 `Objective-C`와 `Swift`에서 각각의 데이터 구조를 비교해봅니다. 31 | 32 | ### Array 33 | - `Array`는 순서를 가지고 있는 배열입니다. `index`를 통해서 각각의 아이템에 접근할 수 있습니다. `index`를 가지고 아이템을 가져오는 것을 `subscripting`이라고 합니다. 34 | - `Objective-C`의 `NSArray`는 `Swift`에서 `let`으로 선언된 `Array`와 같습니다. `NSMutableArray`는 `var`로 선언된 `Array`와 같습니다. 35 | - 특정한 `index`를 가지는 값에 접근할 때의 시간복잡도는, 최악의 케이스는 `O(log n)`이지만 보통 `O(1)`입니다. 36 | - 위치를 모르는 아이템을 찾을 경우의 시간복잡도는, 최악은 `O(n (log n))`이지만 보통 `O(n)`입니다. 37 | - 데이터를 삭제하거나 넣는 경우의 시간복잡도는, 최악은 `O(n (log n))`이지만 보통 `O(1)`입니다. 38 | 39 | ##### 이럴 때 쓰면 좋습니다. 40 | - `item`이 어디 있는지 아는 경우, 검색은 빠를 것입니다. 41 | - `item`이 어디 있는지 모를 경우, 처음부터 끝까지 찾아야 하므로 검색이 느립니다. 42 | - `item`을 어디에 넣고 삭제해야 하는지 알고 있다면 어렵지 않겠지만, 배열의 다른 `item`에도 계속해서 적용해야 하는 경우는 더 긴 시간이 필요합니다. 43 | 44 | >`Swift`는 2015년 12월부터 오픈소스입니다. [Swift Code](https://swift.org/source-code/)로 들어가셔서 구조가 어떻게 되어있는지 확인해보시는 것도 좋은 방법입니다. 45 | 46 | 아래는 `Objective-C`와 `Swift`의 비교 결과입니다. 47 | - Swift의 `Array`와 Foundation의 `NSArray`를 생성하는 시간은 둘 다 `O(log n)`과 `O(n)` 사이의 시간복잡도를 보여줬습니다. 다만 `Array`가 `NSArray`보다 대략 30배나 빨랐습니다. 이것은 Swift 2.3 버전에서는 거의 비슷했던 것에서 상당히 발전한 것입니다! 48 | - 배열의 시작 부분에 아이템을 추가하는 것은 `Array`가 `NSArray`보다 꽤 느렸습니다. (시간복잡도는 `O(1)`) 배열의 중간에 아이템을 추가하는 것은 `Array`가 `NSArray`의 절반밖에 걸리지 않았습니다. 배열의 마지막에 아이템을 추가하는 것은 `O(1)`보다 빠른 결과를 보여준 `Array`가 그렇지 않았던 `NSArray`보다 대략 6배 빠른 속도를 보여줬습니다. 49 | - 아이템을 삭제하는 것은 Swift의 `Array`가 `NSArray`보다 빨랐습니다. 배열의 위치가 처음이건, 중간이건, 마지막이건 `O(log n)`과 `O(n)` 사이의 시간복잡도를 가졌습니다. `Swift`에서는 배열의 위치가 처음일 때 조금 더 빨랐지만, 그 차이는 milliseconds 수준이었습니다. 50 | - 아이템을 참조하는 것은 배열의 앞부분에서는 `Swift`가 빠릅니다. index가 증가할수록 `Array`와 `NSArray` 모두 비슷한 속도로 아주 천천히 증가합니다. 다만 index가 아니라 object를 통한 참조일 때는 `Swift`에서 80배 정도 빠른 속도를 보여줍니다. 51 | 52 | 상기 글은 스위프트가 얼마나 잘 최적화되었는지 보여줍니다. `Swift` 쓰세요. 두 번 쓰세요. 53 | 54 | ### Dictionary 55 | - Dictionary(사전)는 특정 순서와 상관없이 `key`를 통해서 값을 찾을 수 있는 데이터 구조입니다. 56 | - Dictionary는 `subscripting`을 지원하기 때문에, `dictionary["hello"]`와 같은 방법으로 `hello`라는 `key`를 통해서 값에 접근할 수 있습니다. 57 | - `NSDictionary`와 `NSMutableDictionary`의 차이는 `Array`의 경우와 같습니다. 58 | - `Swift`의 Dictionary는 타입을 지정해줘야 합니다. (`NSDictionary`가 `NSObject`를 갖는 것처럼) 59 | 60 | ##### 이럴 때 쓰면 좋습니다. 61 | - 데이터에 순서가 없으면 Dictionary를 사용하시면 됩니다. 62 | - 순서가 아닌 `key`로 접근할 경우 좋습니다. `cats["Ellen"]`과 같이 사용할 경우 Optional이 return 되기 때문에 `if let` 이나 `guard`로 풀어주는 것을 권장합니다. 63 | ```Swift 64 | if let ellensCat = cats["Ellen"] { 65 | print("Ellen's cat is named \(ellensCat).") 66 | } else { 67 | print("Ellen's cat's name not found!") 68 | } 69 | ``` 70 | 71 | 아래는 `Objective-C`와 `Swift`의 비교 결과입니다. 72 | - Swift의 `Dictionary`를 생성하는 것은 `NSMutableDictionary`를 생성하는 것보다 대략 6배 정도 빨랐습니다. 그러나 두 경우 모두 시간복잡도는 `O(n)`으로 차이가 없었습니다. 73 | - Swift의 `Dictionary`에 아이템을 추가하는 것은 `NSMutableDictionary`에 추가할 때보다 100배 정도 빨랐습니다. 시간복잡도는 `O(1)`에 가까운 이상적인 결과를 보여줬습니다. 74 | - Swift의 `Dictionary`에서 아이템을 제거하는 것은 `NSMutableDictionary`에서 제거할 때보다 8배 정도 빨랐습니다. 시간복잡도는 `O(1)`에 가까웠습니다. 75 | - Swift의 `Dictionary`는 참조 역시 빨랐지만, 역시 시간복잡도는 둘 다 `O(1)`였습니다. Swift 3.0은 Foundation보다 상당한 퍼포먼스 향상을 보여준 첫 버전입니다. 76 | 77 | 시간복잡도에서는 차이가 없지만, Swift 3.0에서 상당한 속도 향상이 있었습니다. 78 | 79 | 80 | ### Set 81 | - `Set`(집합)은 순서가 없고 유일한 값을 저장하는 데이터 구조입니다. 중복으로 데이터를 등록할 수 없습니다. 82 | - `Set`에 선언되어있는 값들은 모두 같은 `type` 이어야 합니다. 83 | - `Objective-c` 에서는 각각 `NSSet`과 `NSMutableSet`이 있습니다. 84 | 85 | ##### 이럴 때 쓰면 좋습니다. 86 | - `Set`은 중복이 없는 유일한 값들을 골라낼 때 유용합니다. 87 | - `Snippet`을 하나 보겠습니다. 88 | ```Swift 89 | let names = ["John", "Paul", "George", "Ringo", "Mick", "Keith", "Charlie", "Ronnie"] 90 | var stringSet = Set() // 1 91 | var loopsCount = 0 92 | while stringSet.count < 4 { 93 | let randomNumber = arc4random_uniform(UInt32(names.count)) // 2 94 | let randomName = names[Int(randomNumber)] // 3 95 | print(randomName) // 4 96 | stringSet.insert(randomName) // 5 97 | loopsCount += 1 // 6 98 | } 99 | // 7 100 | print("Loops: " + loopsCount.description + ", Set contents: " + stringSet.description) 101 | ``` 102 | 103 | 1. `Set`을 초기화합니다. 104 | 2. 아이템을 랜덤으로 뽑습니다. 105 | 3. 해당 index에 있는 값을 가져옵니다. 106 | 4. 로그를 남깁니다. 107 | 5. `mutable set`에 값을 저장합니다. 만약 값이 이미 저장되어 있는 경우에는 다시 저장하지 않습니다. 108 | 6. 루프 카운트를 늘려서 루프를 다 돌게 합니다. 109 | 7. 루프가 끝나면 값을 로그에 남깁니다. 110 | 111 | ```Swift 112 | John 113 | Ringo 114 | John 115 | Ronnie 116 | Ronnie 117 | George 118 | Loops: 6, Set contents: ["Ronnie", "John", "Ringo", "George"] 119 | ``` 120 | 로그에는 6개의 값이 찍혔지만, 결론적으로 `Set`에는 4개의 값이 들어가 있습니다. 121 | 122 | 123 | ## 덜 알려진 데이터 구조들 124 | ### NSCache 125 | - `NSCache`를 사용하는 것은 `NSMutableDictionary`를 사용하는 것과 매우 유사합니다. 126 | - 다른 점은 메모리가 낮아질 경우 `NSCache` 데이터는 지워질 수 있습니다. 그리고 항상 재생성됩니다. 127 | >캐시는 화면 뒤에서 비동기적으로 자동으로 메모리에 상주 할지, 남을지 결정합니다. 128 | 129 | ### NSCountedSet 130 | - `NSMutableSet`을 상속받아서 만들어진 데이터 구조로, 얼마나 많은 오브젝트가 `mutable set`에 더해졌는지 알 수 있게 해줍니다. 131 | - 하단 코드를 참고해주세요. 132 | 133 | ```Swift 134 | let countedMutable = NSCountedSet() 135 | for name in names { 136 | countedMutable.add(name) 137 | countedMutable.add(name) 138 | } 139 | 140 | let ringos = countedMutable.count(for: "Ringo") 141 | print("Counted Mutable set: \(countedMutable)) with count for Ringo: \(ringos)") 142 | 143 | Counted Mutable set: {( 144 | George, 145 | John, 146 | Ronnie, 147 | Mick, 148 | Keith, 149 | Charlie, 150 | Paul, 151 | Ringo 152 | )}) with count for Ringo: 2 153 | ``` 154 | ### NSOrderedSet 155 | - `NSOrderedSet`은 개별적인 오브젝트들의 그룹을 순서대로 저장할 수 있게 해줍니다. 156 | >`ordered set`을 `Array`의 대안으로 테스팅할 때 사용해보시면 배열보다 더 빠른 것을 알 수 있습니다. 157 | 158 | ### NSHashTable and NSMapTable 159 | - `NSHashTable`은 `NSMutableSet`과 비슷하지만 몇 가지 다른 점이 있습니다 160 | - `NSHashTable`은 오브젝트 뿐만 아니라 `non-object` 또한 더할 수 있습니다. `NSHashTableOptions`로 메모리 또한 관리할 수 있습니다. 161 | - `NSMapTable`은 dictionary 같은 자료구조이지만, `NSHashTable`과 비슷하게 메모리를 관리한다는 측면이 있습니다. 162 | 163 | --- 164 | 165 | ### Author 166 | 167 | **[younatics 🇰🇷](https://younatics.github.io)** 168 | -------------------------------------------------------------------------------- /helloworld.swift: -------------------------------------------------------------------------------- 1 | print("Hello iOS Developers!") 2 | print("motion-book-ios") 3 | print("motion-book-ios") 4 | print("motion-book-ios") 5 | print("motion-book-ios") 6 | print("motion-book-ios") 7 | print("motion-book-ios") 8 | print("motion-book-ios") 9 | print("motion-book-ios") 10 | print("motion-book-ios") 11 | print("motion-book-ios") 12 | print("motion-book-ios") 13 | print("motion-book-ios") 14 | print("motion-book-ios") 15 | print("motion-book-ios") 16 | print("motion-book-ios") 17 | print("motion-book-ios") 18 | print("motion-book-ios") 19 | print("motion-book-ios") 20 | print("motion-book-ios") 21 | print("motion-book-ios") 22 | print("motion-book-ios") 23 | print("motion-book-ios") 24 | print("motion-book-ios") 25 | print("motion-book-ios") 26 | print("motion-book-ios") 27 | print("motion-book-ios") 28 | print("motion-book-ios") 29 | print("motion-book-ios") 30 | print("motion-book-ios") 31 | print("motion-book-ios") 32 | print("motion-book-ios") 33 | print("motion-book-ios") 34 | print("motion-book-ios") 35 | print("motion-book-ios") 36 | print("motion-book-ios") 37 | print("motion-book-ios") 38 | print("motion-book-ios") 39 | print("motion-book-ios") 40 | print("motion-book-ios") 41 | print("motion-book-ios") 42 | print("motion-book-ios") 43 | print("motion-book-ios") 44 | print("motion-book-ios") 45 | print("motion-book-ios") 46 | print("motion-book-ios") 47 | print("motion-book-ios") 48 | print("motion-book-ios") 49 | print("motion-book-ios") 50 | print("motion-book-ios") 51 | print("motion-book-ios") 52 | print("motion-book-ios") 53 | print("motion-book-ios") 54 | print("motion-book-ios") 55 | print("motion-book-ios") 56 | print("motion-book-ios") 57 | print("motion-book-ios") 58 | print("motion-book-ios") 59 | print("motion-book-ios") 60 | print("motion-book-ios") 61 | print("motion-book-ios") 62 | print("motion-book-ios") 63 | print("motion-book-ios") 64 | print("motion-book-ios") 65 | print("motion-book-ios") 66 | print("motion-book-ios") 67 | print("motion-book-ios") 68 | print("motion-book-ios") 69 | print("motion-book-ios") 70 | print("motion-book-ios") 71 | print("motion-book-ios") 72 | print("motion-book-ios") 73 | print("motion-book-ios") 74 | print("motion-book-ios") 75 | print("motion-book-ios") 76 | print("motion-book-ios") 77 | print("motion-book-ios") 78 | print("motion-book-ios") 79 | print("motion-book-ios") 80 | print("motion-book-ios") 81 | print("motion-book-ios") 82 | print("motion-book-ios") 83 | print("motion-book-ios") 84 | print("motion-book-ios") 85 | print("motion-book-ios") 86 | print("motion-book-ios") 87 | print("motion-book-ios") 88 | print("motion-book-ios") 89 | print("motion-book-ios") 90 | print("motion-book-ios") 91 | print("motion-book-ios") 92 | print("motion-book-ios") 93 | print("motion-book-ios") 94 | print("motion-book-ios") 95 | print("motion-book-ios") 96 | print("motion-book-ios") 97 | print("motion-book-ios") 98 | print("motion-book-ios") 99 | print("motion-book-ios") 100 | print("motion-book-ios") 101 | --------------------------------------------------------------------------------