본문 바로가기

Swift/Swift Language

Swift 언어 가이드 - 컬렉션 타입

컬렉션 타입 (Collection Types)

../_images/CollectionTypes_intro_2x.png

스위프트는 3가지의 주요한 컬렉션 타입을 제공합니다. 컬렉션 타입에는 배열(arrays), 집합(sets), 딕셔너리(dictionaries)있습니다. 컬렉션 타입은 값의 모음(collection)을 저장하기 위해 사용됩니다. 배열은 순서가 있는 값들의 모음입니다. 집합은 순서가 없고 중복이 없는 값들의 모음입니다. 딕셔너리는 순서가 없고 키-밸류(key-value) 조합의 모음입니다.

스위트의 배열, 집합, 딕셔너리는 저장하려는 값과 키의 타입을 항상 명확히 사용합니다. 즉, 여러분은 컬렉션에 잘못된 타입의 값을 추가할 수 없다는 것입니다. 다른 말로 해석하자면 여러분은 컬렉션으로부터 값을 가져올 때 그 값의 타입에 대해 확신을 가져도 좋다는 뜻입니다.

참고

스위프트의 배열, 집합, 딕셔너리는 generic collection으로 구현되었습니다. 더 자세한 사항은 제너릭 (Generics).을 참고하세요.

컬렉션의 가변성(Mutability)

여러분이 배열, 집합, 딕셔너리를 생성하고 변수에 이것을 할당할 때, 컬렉션은 그 값이 변할 수 있습니다. 컬렉션이 가지고 있는 아이템을 바꾸고 삭제하고 변경하여 컬렉션을 변경할 수 있다는 뜻입니다. 만약 배열, 집합, 딕셔너리를 상수에 할당하면 그 컬렉션은 불변합니다. 따라서 크기나 그 컬렉션의 내용을 바꿀 수 없습니다.

참고

컬렉션이 변경될 필요가 없는 경우에는 불변 객체를 생성하는 것이 좋습니다. 스위프트 컴파일러는 여러분이 생성한 컬렉션의 성능을 최적화할 수 있으며, 여러분의 코드가 좀 더 명확한 의도를 가질 수 있기 때문입니다.


배열

배열은 같은 타입의 값들을 순서를 유지하며 저장합니다. 같은 값이 하나의 배열에 다른 위치로 저장될 수 있습니다.

참고

스위프트의 배열은 NSArray 클래스에 연결되어 있습니다.

cocoa와 Foundation 프레임워크에서 Array를 사용한 자세한 정보는 배열과 NSArray 연결하기 (Bridging Between Array and NSArray)를 참고하세요.

약식 문법으로 작성된 배열 타입

스위프트 배열의 타입은 Array형태로 작성됩니다. 여기서 Element는 배열이 저장할 값의 타입입니다. 또는, Element형태로 간략하게 배열 타입을 작성할 수 있습니다. 두 개의 형태는 기능적으로는 동일합니다. 약식 문법이 선호되며 본 가이드 문서는 배열의 타입을 참조할 때 약식 문법을 사용합니다.

빈 배열 생성하기

초기화 문법을 사용하여 특정한 타입의 빈 배열을 작성할 수 있습니다.

var someInts = [Int]()
print("someInts는 [Int]타입이며 \(somInts.count)개의 아이템을 가지고 있다.")
// "someInts는 [Int]타입이며 0개의 아이템을 가지고 있다.

여기서 주의할 점은 someInts변수의 타입은 초기화 문법의 타입인 [Int]로 추론됩니다.

또 다른 방법으로는, 문맥상 이미 타입에 대한 정보가 제공되었다면(예를 들어, 함수 인자 또는 이미 타입이 지정된 변수 혹은 상수), 빈 배열을 빈 배열의 리터럴을 이용하여 생성할 수 있습니다. 이것은 [](비어있는 중괄호 쌍) 으로 작성합니다.

someInts.append(3)
someInts = []
// someInts는 빈 배열이지만 문맥적으로 [Int]임이 추론됩니다.

기본 값을 가진 배열 생성하기

스위프트의 배열은 특정 크기의 배열을 생성하기 위하여 생성자에 값의 집합을 부여할 수도 있습니다. 아래의 예제에서는, repeating이라는 파라미터에 초기값을 지정하고 count라는 값을 반복할 개수를 부여할 수 있습니다.

var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles는 [Double]타입이며 [0.0, 0.0, 0.0]과 같습니다.

두 배열을 더하여 하나의 배열 생성하기

여러분은 (+) 덧셈 연산자를 이용하여 호환 가능한 두 배열을 합하여 새로운 배열을 생성할 수 있습니다. 새로운 배열의 타입은 그 두 개의 배열로부터 추론됩니다.

var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles는 [Double]타입이며 [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles는 [Double]타입으로 추론되며, [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]와 같습니다.

배열 리터럴을 이용하여 배열 생성하기

여러분은 또한 배열 리터럴을 이용하여 배열을 초기화할 수 있습니다. 배열 컬렉션 생성 시 하나 이상의 값을 작성하기 위한 간소화된 방법입니다. 배열 리터럴은 값들의 목록이며 콤마(,)로 구분되어 있으며, 중괄호 쌍으로 둘러싸여 있습니다.

[value1, value2, value3]

아래의 예제는 shoppingList라 불리는 String값을 저장하는 배열을 생성합니다.

var shoppingList: [String] = ["Eggs", "Milk"]
// shoppingList는 두 개의 아이템으로 생성됨

shoppingList 변수는 "문자열 값의 배열", 즉 [String]으로 선언됩니다. 이 배열은 String 값 타입으로 정해졌으므로 String값만 저장할 수 있습니다. 여기에, shoppingList배열은 두 개의 String 값으로 초기화 됩니다.

참고

shoppingList 배열은 변수로 선언되었습니다. 상수가 아닌 이유는 아래의 예제에서 shoppingList에 값을 채워넣기 위함입니다.

배열 리터럴은 두 개의 String값을 가지고 있으며 그외에는 존재하지 않습니다. 이 값의 타입은 shoppingList 변수가 선언될 때의 타입과 일치합니다. 따라서, 배열 리터럴의 할당은 shoppingList를 초기화하는 방식처럼만 허용됩니다.

스위프트의 타입 추론 덕분에, 같은 타입의 값을 가지는 배열 리터럴을 이용하여 초기화하였다면 여러분은 배열의 타입을 작성할 필요가 없습니다. shoppingList의 초기화는 아래와 같이 짧은 형태로 작성될 수 있습니다.

var shoppingList = ["Eggs", "Milk"]

배열 리터럴의 모든 값은 동일한 타입을 가지고 있으므로, 스위프트는 shoppingList 변수의 타입을 [String]으로 추론할 수 있습니다.

배열 접근하기 및 배열 수정하기

배열의 메서드와 속성, 또는 서브스크립트 문법을 이용하여 배열에 접근하거나 수정할 수 있습니다.

배열이 가진 아이템의 개수를 파악하려면, 읽기만 가능한 count속성을 확인합니다.

print("쇼핑 리스트는 \(shoppingList.count)개의 아이템을 가지고 있습니다.")
// 쇼핑 리스트는 2개의 아이템을 가지고 있습니다.

isEmpty 속성을 사용하여 count속성의 값이 0과 같은지 더 간략하게 확인할 수 있습니다.

if shoppingList.isEmpty {
  print("쇼핑 리스트는 비어있습니다.")
} else {
  print("쇼핑 리스트는 비어있지 않습니다.")
}
// "쇼핑 리스트는 비어있지 않습니다."가 출력됨

배열의 append(_:) 메서드를 호출하여 배열의 끝에 새로운 아이템을 추가할 수 있습니다.

shoppingList.append("Flour")
// shoppingList는 이제 3개의 아이템을 가지고 있음

또는, (+=) 덧셈 할당 연산자를 이용하여 하나 이상의 아이템을 추가할 수 있습니다.

shoppingList += ["Baking Powser"]
// shoppingList는 이제 4개의 아이템을 가짐
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// shoppingList는 7개의 아이템을 가짐

서브스크립트 문법을 사용하여 배열의 값을 추출할 수 있습니다. 서브스크립트 문법은 여러분이 추출하기 원하는 값의 인덱스를 필요로 합니다.

var firstItem = shoppingList[0]
// firstItem은 "Eggs"와 같음

주의

배열의 첫번째 번호는 1이 아닌 0으로 시작합니다.

서브스크립트 문법을 사용하여 인덱스에 접근하여 이미 존재하는 값을 변경할 수 있습니다.

shoppingList[0] = "Six eggs"
// 배열의 첫번째 아이템은 이제 "Eggs"가 아닌 "Six eggs"임

서브스크립트 문법을 사용할 때, 여러분이 사용하는 인덱스는 유효해야 합니다. 예를 들어, shoppingList[shoppingList.count] = "Salt"는 존재하지 않는 배열의 번호를 사용하였으므로 런타임 에러가 발생합니다.

여러분은 값의 범위를 한 꺼번에 변경하기 위해 서브스크립트 문법을 사용할 수 있습니다. 이때, 여러분이 바꾸려는 값들의 범위가 기존의 값들의 범위와 달라도 문법이 허용됩니다. 아래의 예제에서 "Chocolate Spread", "Cheese"의 값으로 "Butter""Bannas", "Apples"을 바꿀 수 있습니다.

shoppingList[4..6] = ["Bananas", "Apples"]
// shoppingList는 이제 6개의 아이템을 가짐

배열에 아이템을 추가하려면 insert(_:at:) 메서드를 호출합니다.

shoppingList.insert("Maple Syrup", at: 0)
// shoppingList는 7개의 아이템을 가짐
// "Maple Syrup"은 이제 리스트의 첫 번째 아이템이 됨

insert(_:at:)메서드를 호출하여 새로운 아이템인 "Maple Syrup"을 배열의 첫 번째에 추가하였습니다.

이와 비슷하게, remove(at:)메서드를 이용하여 아이템을 제거할 수 있습니다. 이 메서드는 아이템을 제거하고 제거된 해당 아이템을 반환합니다.

let mappleSyrup = shoppingList.remove(at: 0)
// 아이템은 0 인덱스에 있었고 제거됨
// shoppingList는 이제 6개의 아이템을 가짐
// mapleSyrup 상수는 이제 "Maple Syrup"이라는 문자열과 동일함

참고

만약 여러분이 배열의 범위를 벗어난 인덱스에 있는 값을 접근하거나 수정하려 할 때, 런타임 에러가 발생하게 됩니다. 배열의 count속성을 이용하여 여러분이 사용하려는 인덱스가 유효한지 확인할 수 있습니다. 배열의 인덱스 중 가장 크면서 유효한 값은 count - 1입니다. 왜냐하면 배열은 0부터 시작하기 때문입니다. 하지만 count가 0이면 어떠한 인덱스도 유효하지 않습니다.

배열의 가장 마지막에 있는 아이템을 제거하고 싶다면 remove(at:) 메서드 대신, removeLast() 메서드를 사용하세요. remove(at:)메서드 처럼 removeLast()는 제거된 아이템을 반환합니다.

let applpes = shoppingList.removeLast()
// 마지막 아이템인 "Apples"는 제거됨
// shoppingList는 이제 5개의 아이템을 가짐
// apples 상수는 "Apples" 문자열과 같음

배열 순회하기

for-in 반복문을 사용하여 배열의 모든 값을 순회할 수 있습니다.

for item in shoppingList {
  print(item)
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas

만약 배열의 값뿐만 아니라, 인덱스 값도 필요하다면, enumerated()메서드를 사용하여 배열을 순회하세요. enumerated()메서드는 아이템의 인덱스 정수와 값을 튜플로 반환합니다. 배열은 0부터 시작하여 각 아이템마다 하나씩 더해집니다. 반환되는 튜플을 상수나 변수에 분해할 수 있습니다.

for (index, value) in shoppingList.enumerated() {
  print("Item \(index + 1): \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powser
// Item 5: Bannas

for-in 반복문에 대한 자세한 내용은 For-In Loops을 참고하세요.

집합 Sets

_집합(set)_은 하나의 컬렉션 안에서 같은 타입의 서로 구별되는 값을 저장합니다. 또한 이 컬렉션은 정해진 순서 없이 값을 저장합니다. 여러분은 집합을 아이템의 순서가 중요하지 않을 때 배열 대신 사용할 수 있습니다. 또는 아이템이 오로지 한 번만 저장된다면 집합을 사용할 수 있습니다.

참고

스위프트의 Set타입은 Foundation 라이브러리의 NSSet클래스와 연결되어 있습니다.

Cocoa와 Foundation의 Set에 대한 자세한 사항은 Bridging Between Set and NSSet을 참고하세요.

집합 타입을 위한 해시 값

집합에 어떠한 타입을 저장하기 위해서는 반드시 그 타입은 해시가 가능해야 합니다. 즉, 타입은 타입 자신을 위해 해시 값을 계산하는 방법을 제공해야 한다는 것입니다. 해시 값은 Int값으로 모든 객체를 동등하게 비교할 수 있습니다. 예를 들어 a == b라면 이것은 a.hashValue == b.hashValue와 동일합니다.

모든 스위프트의 기본 타입 (예를 들어 String, Int, Double 그리고 Bool)은 기본적으로 해시가 가능합니다. 따라서, 집합 값의 타입이나 딕셔너리의 키 타입으로 사용할 수 있습니다. 열거형의 값은 연관된 값이 없을 경우에 기본적으로 해시가 가능합니다.

참고

여러분이 직접 작성한 타입을 집합 값의 타입이나 딕셔너리 키의 타입으로 사용할 수 있습니다. 단, 스위프트의 표준 라이브러리에 있는 Hashable 프로토콜에 따라야 합니다. Hashable 프로토콜을 따르는 타입은 반드시 해시 값을 반환하는 Int 프로퍼티를 제공해야 합니다. 그 값은 다른 프로그램 혹은 같은 프로그램의 다른 실행 환경에서 필수적이지 않습니다.

Hashable 프로토콜은 Equatable 프로토콜을 따르므로, 동등 연산자인 ==을 구현해야 합니다. Equatable 프로토콜은 모든 == 연산자의 구현을 필요로 합니다. 즉, ==의 구현은 다음의 3가지 조건을 만족해야 합니다.

  • a == a (재귀성 Reflexivity)
  • a == bb == a이다.(대칭성 Symmetry)
  • a == b && b == ca == c와 같다 (전이성 Transitivity)

프로토콜 적합성에 대한 자세한 사항은 Protocols을 참고하세요.

집합 타입 문법

스위프트 집합의 타입은 Set<Element>로 작성합니다. 여기서 Element는 집합이 저장할 수 있는 타입을 말합니다.배열과 다르게, 집합은 축약된 형식이 존재하지 않습니다.

비어 있는 집합 생성 및 초기화하기

특정한 타입을 가진 비어 있는 집합을 생성자 문법으로 생성할 수 있습니다.

var letters = Set<Character>()
print("letters 변수는 Set<Character>의 타입으로 \(letters.count) 개의 아이템을 갖는다.")
// letters 변수는 Set<Character>의 타입으로 0 개의 아이템을 갖는다

참고

letters 변수의 타입은 생성자의 타입에 의해 Set<Character>로 추론되었습니다.

다른 방법으로는, 문맥 상 타입 정보가 제공되었다면 비어 있는 배열 리터럴을 가지고 비어있는 집합을 생성할 수 있습니다.

letters.insert("a")
// letters는 이제 Character 타입의 1개의 값을 가짐
letters = []
// letters는 빈 집합이지만, 여전히 Set<Character> 타입임

배열 리터럴을 이용하여 집합 생성하기

여러분은 또한 배열 리터럴을 이용하여 집합을 초기화할 수 있습니다. 이 방법은 하나 이상의 값을 집합 컬렉션으로 축약하여 작성하는 방법입니다.

아래의 예제는 String 값을 저장하는 favoriteGenres라고 불리는 집합입니다.

var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres는 3 개의 아이템으로 초기화됨

favoriteGenres 변수는 "String값의 집합"으로 선언되었고 이것을 Set<String>으로 작성하였습니다. 이 특정한 집합은 값의 타입이 String으로 명시하였으므로, 오로지 String 값만을 저장할 수 있습니다. 여기서, favoriteGenre' 집합은 3개의String` 값인 ("Rock", "Classical", "Hip hop")으로 초기화되었고, 배열 리터럴안에서 작성되었습니다.

주의

favoriteGeners 집합은 var 키워드를 이용하여 변수로 선언되었습니다. 이후의 예제에서 아이템들을 추가하고 삭제하기 위함입니다.

집합 타입은 오로지 배열 리터럴만을 이용하여 추론될 수 없습니다. 따라서, Set의 타입은 명시적으로 선언되어야 합니다. 하지만, 스위프트의 타입 추론을 이용하면 하나의 타입을 가지는 배열 리터럴로 집합의 타입을 추론할 수 있습니다. favortieGenres의 초기화는 다음과 같이 간략하게 작성할 수 있습니다.

var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]

배열 리터럴의 모든 값은 동일한 타입을 가졌으므로 스위프트는 favoriteGenres 변수에 Set<String>으로 타입을 추론할 수 있습니다.

집합에 접근하고 집합을 수정하기

여러분은 집합의 메서드와 속성을 사용하여 집합에 접근하거나 집합을 수정할 수 있습니다.

집합 안의 아이템의 개수를 파악하려면 읽기전용 속성인 count를 확인하세요.

print("제가 좋아하는 뮤직 장르는 \(favoriteGenres.count) 가지 입니다.")
// "제가 좋아하는 뮤직 장르는 3 가지 입니다."가 출력됨

count 속성이 0인지 아닌지를 간단하게 확인하려면 불리언 값을 리턴하는 isEmpty프로퍼티를 사용합니다.

if favoriteGenres.isEmpty {
  print("음악에 관해선, 나는 까다롭지 않아")
} else {
  print("나는 특정한 음악만을 선호해")
}
// "나는 특정한 음악만을 선호해"가 출력돔

여러분은 insert(_:) 메서드를 호출하여 집합에 새로운 아이템을 추가할 수 있습니다.

favoriteGenres.insert("Jazz")
// favoriteGenres는 이제 4 개의 아이템을 가짐

집합의 아이템을 제거하려면 remove(_:)메서드를 호출합니다. 이 메서드는 집합에 있어야만 삭제할 수 있습니다. 또한 이 메서드는 삭제된 값을 반환하거나, 삭제하려는 값이 집합에 존재하지 않는다면 nil을 반환합니다. removeAll()메서드를 사용하면 집합의 모든 아이템을 삭제할 수 있습니다.

if let removedGenre = favoriteGenres.remove("Rock") {
  print("\(removedGenre)은 질렸어")
} else {
  print("별로 신경쓰지 않아")
}
// "Rock은 질렸어" 출력됨

집합이 특정한 아이템을 가졌는지 확인하려면 contains(_:) 메서드를 사용합니다.

if favoriteGenres.contains("Funk") {
  print("Funk는 있다.")
} else {
  print("Funk는 없다")
}
// "Funk는 없다"가 출력됨

집합 순회하기

for-in 반복문으로 집합안의 값을 순회할 수 있습니다.

for genre in favoriteGenres {
  print("\(genre)")
}
// Classical
// Jazz
// Hip hop

스위프트의 Set 타입은 순서를 가지고 있지 않습니다. 집합의 값을 정해진 순서로 순회하려면 sorted() 메서드를 사용합니다. 이 메서드는 집합의 원소를 < 연산자를 이용하여 정렬된 배열로 반환하여 줍니다.

for genre in favoriteGenres.sorted() {
  print("\(genre)")
}
// Classical
// Hip hop
// Jazz

집합 연산하기

여러분은 스위프트에서 기초적인 집합 연산을 효과적으로 할 수 있습니다. 예를 들어, 두 개의 집합을 합치거나, 두 집합이 공통적으로 가진 것을 판별하거나, 두 집합이 모두 가지고 있는 것 혹은 일부만 가진 것, 가지고 있지 않은 것을 파악할 수 있습니다.

기초적인 집합 연산

아래의 그림은 두 개의 집합, ab를 도식화한 것입니다. 색이 칠해진 영역이 집합 연산의 결과입니다.

../_images/setVennDiagram_2x.png

  • intersection(_:) 메서드는 두 집합의 공통 부분을 새로운 집합으로 생성합니다.
  • symmetricDifference(_:) 메서드는 공통인 부분을 제외하고 두 집합에 있는 값을 새로운 집합으로 생성합니다.
  • union(_:) 메서드는 두 집합에서 나올 수 있는 모든 값을 새로운 집합으로 생성합니다.
  • subtracting(_:) 메서드는 특정 집합에 존재하지 않는 값을 새로운 집합으로 생성합니다.
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]

oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(eventDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimNumbers).sorted()
// [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]

집합 구성요소와 동등성

아래의 그림은 세 개의 집합, a, b, c을 도식화하여 표현했습니다. 집합 간의 같은 원소는 덧대어 그려져 있습니다. 집합 ab의 _슈퍼셋(superset)_입니다. 왜냐하면 ab의 모든 원소를 가지고 있기 때문입니다. 반대로 집합 b는 집합 a의 _서브셋(subset)_입니다. b의 원소가 a에 모두 포함되어 있습니다. 그리고, 집합 b와 집합 c는 _disjoint_입니다. 두 집합 간의 공유되는 원소가 없습니다.

../_images/setEulerDiagram_2x.png

  • "is equal" 연산자인 (==)을 사용하여 두 집합이 같은 값을 가지는지 확인할 수 있습니다.
  • isSubset(of:) 메서드를 사용하여 하나의 집합이 다른 집합에 포함되는지 확인할 수 있습니다.
  • isSuperset(of:) 메서드를 사용하여 하나의 집합이 다른 집합의 원소를 모두 가지는지 확인할 수 있습니다.
  • isStrictSubset(of:) 혹은 isStrictSuperset(of:) 메서드를 사용하면 집합이 서브셋인지 슈퍼셋인지 확인 할 수 있습니다.
  • isDisjoint(with:)을 사용하여 두 집합이 어떠한 같은 값도 가지지 않는지 확인할 수 있습니다.
let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]

houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAniamsl.isDisjoint(with: cityAnimals)
// true

딕셔너리 Dictionaries

_딕셔너리(dictionary)_는 같은 타입의 키들과 같은 타입의 값들의 쌍을 컬렉션에 저장합니다. 또한 딕셔너리는 정해진 순서가 없습니다. 각각의 값은 유일한 키 값에 연결되어 있습니다. 키 값은 딕셔너리 안에서 값에 대한 식별자 역할을 합니다. 이러한 식별자를 가지고 값을 찾으려 할 때 딕셔너리를 사용합니다. 현실 세계의 사전(딕셔너리)과 유사하게도, 특정한 단어의 뜻을 찾는 것입니다.

참고

스위프트의 Dictionary 타입은 Foundation 라이브러리의 NSDictionary 클래스에 연결되어 있습니다.

Foundation과 Cocoa를 이용한 Dictionary에 대한 자세한 내용은 Bridging Between Dictionary and NSDictionary을 참고하세요.

간략한 딕셔너리 타입 문법

스위프트의 딕셔너리 타입은 Dictionary<Key, Value>로 작성합니다. 여기서 Key는 값의 타입으로 딕셔너리의 키를 의미하며, Value는 그 키에 대한 딕셔너리가 저장할 값을 의미합니다.

주의

딕셔너리의 Key 타입은 Set의 값 타입처럼, 반드시 Hashable 프로토콜을 따라야 합니다.

또한, 여러분은 딕셔너리를 [Key: Value] 형태로 작성할 수 있습니다. 위의 방식과는 기능적으로 동일하지만 짧은 문법의 형태가 선호되며 이후 가이드에서는 이 방식을 따릅니다.

비어있는 딕셔너리 생성하기

배열과 유사하게, 비어있는 Dictionary를 특정한 타입으로 생성하려면 다음의 초기화 문법을 사용합니다.

var namesOfIntegers = [Int: String]()
// namesOfIntegers는 빈 [Int: String] 딕셔너리임

타입이 [Int: String]인 빈 딕셔너리를 생성하여 사람이 읽기 쉬운 정수 값을 저장하는 예제입니다. 키가 Int라면 값은 String 타입입니다.

만약 문맥적으로 타입 정보가 제공되면 빈 딕셔너리를 빈 딕셔너리 리터럴을 이용하여 생성할 수 있습니다. 콜론을 감싸는 대괄호로 작성합니다.

namesOfIntegers[16] = "sixteen"
// namesOfIntegers는 이제 1개의 키-밸류 쌍을 가짐
namesOfIntegers = [:]
// namesOfIntegers는 타입이 [Int: String]인 빈 딕셔너리임

딕셔너리 리터럴을 사용하여 딕셔너리 생성하기

딕셔너리 리터럴을 이용하여 딕셔너리를 초기화할 수 있습니다. 이것은 이전에 소개한 배열 리터럴과 유사한 문법입니다. 딕셔너리 리터럴은 하나 이상의 키-값 쌍을 Dictionary 컬렉션으로 선언하는 간략한 방법입니다.

키-값 쌍은 키와 값을 합친 것입니다. 딕셔너리 리터럴에서 각각의 키-값 쌍에서 키와 값은 콜론으로 구분됩니다. 키-값 쌍은 리스트로 작성되며 콤마로 구분합니다. 그리고 대괄호를 이용하여 전체를 감쌉니다.

[key 1: value 1, key 2: value 2, key 3: value 3]

아래의 예제는 딕셔너리가 국제 공항의 이름을 저장합니다. 이 딕셔너리에서는 키가 세글자의 국제 항공 운송 협회 코드를 뜻하며 값은 공항의 이름을 뜻합니다.

var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]

airports 딕셔너리는 [String: String]의 타입을 가진채로 선언되었습니다. 여기서 키는 String 타입이며 값또한String`입니다.

참고

airports 딕셔너리는 var를 이용하여 변수로 선언되었습니다. 아래의 예젱서 더 많은 공항들이 추가되기 위함입니다.

airports 딕셔너리는 두 개의 키-값 쌍을 포함한 딕셔너리 리터럴로 초기화되었습니다. 첫 번째 쌍의 키는 "YYZ"이고 값은 "Toronto Pearson"입니다. 두 번째 쌍의 키는 "DUB"이고 값은 "Dublin"입니다.

딕셔너리 리터럴은 두 개의 String: String 쌍을 갖습니다. 키-값 타입은 airports변수가 선언된 타입과 일치합니다. 따라서 딕셔너리 리터럴이 허용되는 것입니다.

배열과 더불어, 여러분은 딕셔너리 리터럴을 이용하여 딕셔너리를 초기화할 때, 딕셔너리의 타입을 작성할 필요가 없습니다. airports의 초기화는 다음과 같이 짧게 작성할 수 있습니다.

var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]

리터럴의 모든 키는 같은 타입입니다. 그리고 값 또한 같은 타입을 가지고 있습니다. 따라서, 스위프트는 airports 딕셔너리를 올바른 타입인 [String: String]으로 추론할 수 있는 것입니다.

딕셔너리에 접근하고 수정하기

딕셔너리의 메서드와 속성을 사용하여 딕셔너리에 접근하거나 수정할 수 있습니다. 또는 서브스크립트 문법을 사용할 수 있습니다.

배열과 함께, Dictionary 안의 아이템의 개수를 확인하려면 count 속성을 사용합니다.

print("airport 딕셔너리는 \(airports.count) 개의 아이템을 가진다.")
// "airport 딕셔너리는 \(airports.count) 개의 아이템을 가진다."가 출력됨

count 속성이 0인지를 확인할 때, isEmpty 속성을 사용하면 더 간략하게 확인할 수 있습니다.

if airports.isEmpty {
  print("airports dictionary가 비어있음")
} else {
  print("airpots dictionary가 비어있지 않음")
}
// "airpots dictionary가 비어있지 않음"이 출력됨

딕셔너리에 새로운 아이템을 추가할 때 서브스크립트 문법을 사용할 수 있습니다. 타입에 맞는 새로운 키를 서브스크립트의 인덱스로 작성합니다. 그리고 타입에 맞는 새로운 값을 할당합니다.

airpots["LHR"] = "London"
// airports 딕셔너리는 3 개의 아이템을 가짐

또한, 서브스크립트 문법을 이용하면 특정 키에 연결된 값을 바꿀 수 있습니다.

airpots["LHR"] = "London Heathrow"
// "LHR"의 값은 "London Heathrow"로 바뀜

서브스크립트 대신에, updateValue(_:forKey:) 메서드를 사용하여 특정 키에 대한 값을 생성하거 수정할 수 있습니다. 위의 서브스크립트 예제와 마찬가지로, updateValue(_:forKey:) 메서드는 키가 존재하지 않을 때 새로운 값을 설정하며, 키 값이 이미 존재하면 값을 수정합니다. 하지만 서브스크립트와 다른 점은 updateValue(_:forKey:)메서드는 키에 대한 값을 수정을 할 때 이전 값을 반환한다는 점입니다. 이 메서드를 활용하면 수정이 발생했는지 안했는지를 확인할 수 있습니다.

updateValue(_:forKey:) 메서드는 딕셔너리 값 타입을 옵셔널 값으로 반환합니다. 예를 들어, 딕셔너리가 String값을 저장한다면 메서드는 String? 또는 optional String을 반환합니다. 만약 수정 이전에 어떠한 값이 존재했다면 옵셔널 값은 키에 대한 이전 값을 가지고 있습니다. 혹은 존재하지 않았다면 nil을 반환합니다.

if let oldvalue = airports.updateValue("Dublin Airports", forKey: "DUB") {
  print("DUB의 이전 값은 \(oldValue)이다. ")
}
// "DUB의 이전 값은 Dublin 이다."가 출력됨

서브스크립트 문법을 사용하여서 특정 키에 대한 딕셔너리의 값을 가져올 수 있습니다. 값이 존재하지 않는 키를 가져올 수도 있으므로 딕셔너리의 서브스크립트는 딕셔너리 값 타입을 옵셔널 값으로 반환합니다. 만약, 딕셔너리가 요청된 키에 대해 값을 가진다면 서브스크립트는 그 키에 대한 해당 값을 포함한 옵셔널 값을 반환합니다. 그렇지 않다면, 서브스크립트는 nil을 반환합니다.

if let airpotName = airpots["DUB"] {
  print("공항의 이름은 \(airpotName)이다.")
} else {
  print("그 공항은 airports 딕셔너리에 없다. ")
}
// "공항의 이름은 Dublin Airport이다."가 출력됨

서브스크립트 문법을 사용하여 딕셔너리의 키-값 쌍을 삭제할 수 있습니다. 이 때 nil값을 할당해야 합니다.

airpots["APL"] = "Apple Internationals"
// "Apple Internationals"은 APL의 실제 공항이 아니므로 지운다.
airports["APL"] = nil
// APL은 딕셔너리에서 삭제되었다.

이와 다른 방법으로는, 딕셔너리의 키-값쌍을 removeValue(forKey:) 메서드를 이용하여 삭제할 수 있습니다. 이 메서드는 이미 키-값 쌍이 존재한다면 삭제된 값을 반환하고 그렇지 않다면 nil을 반환합니다.

if let removedValue = airports.removeValue(forKey: "DUB") {
  print("삭제된 공항의 이름은 \(removedValue)이다.")
} else {
  print("그 공항의 딕셔너리는 DUB에 맞는 값을 가지고 있지 않다.")
}
// "삭제된 공항의 이름은 Dublin Airport이다."가 출력됨

딕셔너리 순회하기

for-in 반복문을 이용하면 딕셔너리의 키-값 쌍을 순회할 수 있습니다. 딕셔너리의 각각의 아이템은 (key, value) 튜플로 반환되며 튜플의 원소를 임시로 상수나 변수에 분해할 수 있습니다.

for (airportCode, airportName) in airports {
  print("\(airportCode): \(airportName)")
}
// LHR: London Heathrow
// YYZ: Toronto Pearson

딕셔너리의 keysvalues 속성에 접근하면 딕셔너리의 키 혹은 값의 순회 가능한 컬렉션을 가져올 수 있습니다.

for airportCode in airports.keys {
  print("Airport code: \(airportCode)")
}
// Airport code: LHR
// Airport code: YYZ

for airportName in airports.values {
  print("Airport name: \(airportName)")
}
// Airport name: London Heathrow
// Airport name: Toronto Pearson

만약 여러분이 딕셔너리의 키 또는 값을 Array 인스턴스를 생성하는 API로 사용할 수 있습니다.

let airportCodes = [String](airports.keys)
// airportCodes는 ["LHR", "YYZ"]임
let airportNames = [String](airports.values)
// airportNames는 ["London Heathrow", "Toronto Pearson"]임

스위프트의 Dictionary타입은 순서를 정하지 않습니다. 키 혹은 값을 정해진 순서에 따라 순회하려면 sorted()메서드를 keys, values속성에 사용하세요.