Search

[Swift] Collection Types - Dictionary

The Swift Programming Language Documents를 참고해서 정리했습니다. 여러 가지 생각들이 버무러져 있습니다.
contents
*이전 포스팅은 [Swift] Collection Types - Set 에서 보실 수 있습니다.

Collection Types

Swift에서는 Value 콜렉션을 저장하기 위한 3가지 기본 타입을 제공합니다.
Array : 순서가 지정된 값 콜렉션
Set : 고유값의 순서가 지정되지 않은 콜렉션
Dictionary : Key-Value 연결의 순서가 지정되지 않은 콜렉션
3가지 Collection은 저장할 수 있는 Value, Key 타입이 항상 명확합니다. 이는 잘못된 타입의 값을 실수로 Collection에 삽입할 수 없다는걸 의미합니다. 또한, Collection에서 값을 찾을 때 값이 가지는 타입에 대해 확신할 수 있음을 의미합니다.
Collection Type을 변수에 할당하게 되면, Collection 내부에 Item를 추가, 제거, 변경하는 방식으로 Collection를 변경할 수 있게 됩니다. 하지만, 상수에 할당하게 되면 Collection의 크기나 내용을 변경할 수 없게 됩니다.
var shoppingList: [String] = ["Six Eggs", "Milk", "Flour", "Bananas"] let genres: Set<String> = ["Rock", "Jazz", "Classical", "Hip Hop"]
Swift
복사
Collection를 초기화한 후에 변경할 필요가 없다면, 상수로 선언해서 immutable로 만드는 것이 좋습니다.
상수로 선언된 Collection은 코드를 쉽게 추론 가능하게 하고, Swift 컴파일러가 작성된 Collection의 성능 최적화 가능하게 만들어 줍니다.

Dictionary

Dictionary는 요소가 Key-Value 쌍인 정의된 순서가 없는 콜렉션입니다.
각 Value는 고유 Key와 연관되어 있어서, Value의 식별자 역할을 하는 Key를 사용해서 Value를 찾아낼 수 있습니다.
해당 방식은 실제로 사전에서 원하는 단어를 찾는 방식과 동일합니다. 단어의 식별자 역할을 하는 알파벳을 사용해서 대략적인 단어의 위치를 찾을 수 있기 때문이죠.
사전에서 알파벳을 사용해서 대략적인 단어의 위치를 찾는 방식은 앞에서부터 차례대로 보면서 단어를 찾는 방식보다 훨씬 빠른 속도로 단어를 찾을 수 있을겁니다. Dictionary에서 Key를 사용해서 Value를 찾는 방식도 앞에서부터 차례대로 인덱스를 찾아가는 배열보다 빠른 속도로 원하는 Value를 찾을 수 있습니다.
왜 Dictionary가 더 빠른가요?
Dictionary는 해시 테이블의 한 종류입니다.
따라서, Set의 Value Type처럼 Key Value 항목을 해시 타입의 키를 사용해서 식별합니다. 해시 값을 인덱스처럼 사용할 수 있기 때문에 빠른 검색이 가능해집니다. 좀 더 자세한 내용은 Collection Types - Set에 있는 Set Element 섹션 부분에서 보실 수 있습니다.
그럼, 이제 Dictionary를 사용해봅시다.
Dictionary는 3가지 방식으로 선언할 수 있습니다. ⓵ 기본형, ⓶ 축약형, ⓷ 타입 추론을 사용해서 선언합니다. 기능적으로 기본형과 축약형이 동일하지만 축약형을 선호한다고 공식 문서에 적혀있습니다.
⓵ 기본형
var shoppingList = Dictionary<String, String>()
Swift
복사
⓶ 축약형
var shoppingList: [String, String] = [:]
Swift
복사
⓷ 타입 추론
var shoppingList = ["Milk": 3000, "Apple": 1000, "Cake": 20000] // Dictionary Literal에 있는 Key, Value 타입이 같으면 해당 타입으로 타입 추론
Swift
복사
⓷ 타입 추론을 통해서 shoppingList를 만들었다고 치고, 새로운 물건을 더 넣어봅시다.
Dictionary는 서브스크립트를 사용해서 새로운 값을 Dictionary에 추가할 수 있습니다. 서브스크립트의 인덱스 자리에는 Key로 사용할 요소를 넣고 Value를 해당 Key에 넣는 형식으로 작성해주시면 됩니다.
shoppingList["Salad"] = 5000
Swift
복사
해당 방식말고도 updateValue 메서드를 사용해서 값을 추가할 수 있습니다.
해당 메서드를 사용하면 특정 Key에 대한 값을 설정하거나 업데이트할 수 있습니다. 해당 Key가 Dictionary에 없었다면 새로 추가하고, 이전에 있었다면 값을 업데이트하고 이전 Value를 반환합니다. 이를 통해서 업데이트 여부를 알 수 있습니다.
새로운 Key 추가
if let oldValue = shoppingList.updateValue(12000, forKey: "Chicken") { print("update \(oldValue) to newValue") } else { print("Create New Value") } /* Create New Value */
Swift
복사
이전에 있던 Key 업데이트
if let oldValue = shoppingList.updateValue(2000, forKey: "Apple") { print("update \(oldValue) to newValue") } else { print("Create New Value") } /* update 1000 to newValue */
Swift
복사
아이템을 추가했다면, 이번엔 삭제도 해봅시다.
Dictionary는 서브스크립트를 사용해서 값을 삭제할 수 있습니다. Key에 해당하는 Value로 nil을 넣어주면 됩니다.
shoppingList["Milk"] = nil
Swift
복사
서브스크립트말고도 removeValue 메서드를 사용해서 제거할 수 있습니다. updateValue처럼 removeValue도 해당 Key가 없으면 nil을 반환하고, 있으면 삭제된 Value를 반환합니다.
if let removeValue = shoppingList.removeValue(forKey: "Apple") { print("remove \(removeValue)") } /* remove 1000 */
Swift
복사
이제 쇼핑 목록에 있는 물품들을 전체적으로 출력해서 보고 싶네요.
해당 기능은 for-in 루프를 사용해서 구현해봅시다. Dictionary는 기본적으로 튜플 형식으로 값을 내보냅니다. 따라서, 값을 출력해보면 Key-Value를 담고 있는 튜플 형식으로 되어 있습니다.
for element in shoppingList { print(element) } /* (key: "Salad", value: 5000) (key: "Chicken", value: 12000) (key: "Cake", value: 20000) */
Swift
복사
만약, Key와 Value를 따로 받고 싶다면 element를 튜플 형식으로 바꾸면 됩니다.
for (key, value) in shoppingList { print("\(key) \(value)") } /* Cake 20000 Salad 5000 Chicken 12000 */
Swift
복사
또한, Key와 Value를 따로 따로 출력할 수도 있습니다. Dictionary가 가지고 있는 Keys, Values 속성을 사용하면 됩니다.
for element in shoppingList.keys { print(element) } /* Salad Cake Chicken */
Swift
복사
for element in shoppingList.values { print(element) } /* 20000 12000 5000 */
Swift
복사
Dictionary는 순서를 가지지 않는 콜렉션이기 때문에 keys, values 콜렉션을 순서대로 받고 싶다면 sorted 메서드를 사용하면 됩니다. 해당 메서드를 사용하면 순서대로 정렬된 배열을 받을 수 있습니다.
for element in shoppingList.values.sorted() { print(element) } /* 5000 12000 20000 */
Swift
복사
가끔, Dictionary를 사용하면서 인덱스를 사용해서 특정 값을 찾고 싶을 수도 있습니다. Key 말구요.
이런 경우라면 firstIndex(where:)라는 메서드를 사용해서 Index를 찾을 수 있습니다.
if let index = shoppingList.firstIndex(where: { $0.key == "Cake" }) { print(shoppingList[index]) } /* (key: "Cake", value: 20000) */
Swift
복사
하지만, Index를 사용했을 때 원하지 않는 값을 반환할 수도 있습니다.
왜 원하지 않는 값을 반환하나요?
Dictionary가 추가된 값을 저장할 수 있는 충분한 용량을 가지면 Dictionary의 index가 유효하게 유지됩니다. 하지만, Dictionary의 버퍼가 초과하게 되면 기존 인덱스가 통보없이 무효될 수 있습니다.
만약, 추가할 Value의 수를 안다면, init(minimumCapacity:)를 사용하여 올바른 양의 버퍼를 할당할 수 있습니다.

KeyValuePairs

만약, Key-Value에 순서가 지정되고, Dictionary가 제공하는 빠른 Key 조회가 필요하지 않다면 Dictionary 대신 사용할 수 있는 타입이 있습니다.
바로, “KeyValuePairs” 입니다.
해당 타입은 순서가 지정되어 있고, 중복된 키 사용도 허용됩니다.
대신, Dictionary에서 효율적인 일부 작업이 KeyValuePairs를 사용할 때 속도가 느립니다.
KeyValuePairs는 Key와 일치하는 값을 찾기 위해서 Collection의 모든 요소를 검색합니다. 그렇기 때문에, 해시 값을 인덱스로 사용해서 값을 찾는 Dictionary보다 느릴 수 밖에 없습니다.
또한, Key만 hashable을 준수하면 되는 Dictionary와는 다르게 Key, Value 모두 hashable을 준수해야 합니다.
KeyValuePairs는 Dictionary 선언하듯 선언할 수 있고, 전달된 순서와 중복된 값을 보존합니다.
var pairs: KeyValuePairs = ["Milk": 3000, "Apple": 1000, "Cake": 20000, "Cake": 10000] print(pairs) /* ["Milk": 3000, "Apple": 1000, "Cake": 20000, "Cake": 10000] */
Swift
복사
KeyValuePairs 타입은 Dictionary, Set, Array같은 콜렉션과는 다르게 remove, insert같은 추가, 삭제 관련 메서드가 없더라구요. 특정 값을 검색해온다던가 하는 작업은 가능하지만 원래 있던 KeyValuePairs 타입에 새로운 값을 추가하거나 있던 값을 삭제하는건 불가능한 거 같습니다.🫤

️ 참고 자료