Swift 는 3개의 주요 컬렉션 (Collection) 자료형 Array
, Set
, Dictionaries
을 제공합니다. Array
는 값을 지니는 정렬된 컬렉션이며, Set
과 Dictionaries
는 비정렬 컬렉션으로, 각각 고유의 값을 지니거나 Key-Value
로 이루어진 컬렉션입니다.
Swift 에서 각 컬렉션들은 지닐 수 있는 자료형이 명백하게 정의되기에, 사용자는 잘못된 형태의 값을 컬렉션에 넣을 수 없게 됩니다. 또한 각각의 컬렉션은 컬랙션 내에서 동일한 자료형만 반복될 것임을 확신할 수 있게 됩니다.
Array
, Set
, Dictionaries
를 만들 때 변수(variable)로 만들게 되면 해당 컬렉션은 변경 및 수정이 가능하게 되며, 이와 반대로 상수(constant)로 만들게되면 초기화를 할 때를 제외하면 변경 및 수정이 불가능하게 됩니다.
array
는 같은 자료형으로 정렬된 목록의 값들을 저장하는 컬렉션입니다.
Note: Swift 의 배열은 Foundation 프레임워크의 NSArray class 와 연결되어 있습니다. Array 를 Foundation 과 Cocoa 를 통해 사용하고 싶다면 Bridging Between Array and NSArray 를 참고해주세요.
Swift Array 자료형은 Array<Element>
의 형태로 정의됩니다. 이때 짧게 사용하는 방법으로 [Element]
를 통해 사용할 수 있습니다. 두 형태는 기능적으로 동일하며, 이하 Swift Language Guide 에서는 줄임 표현을 사용할 것 입니다.
빈 배열은 타음과 같이 만들 수 있습니다.
var someInts: [Int] = []
print("someInts 는 [Int] 자료형으로 \(someInts.count) 개수 만큼 원소를 갖고 있습니다.")
// "someInts 는 [Int] 자료형으로 0 개수 만큼 갖고 원소를 갖고 있습니다."
배열이 variable 에 Int 자료형으로 선언되었으므로 다음과 같이 사용할 수 있습니다.
someInts.append(3)
// someInts 는 이제 Int 자료형의 원소를 1개 갖고 있습니다.
someInts = []
// someInts 는 이제 빈 배열이지만, 여전히 [Int] 형입니다.
Swift 의 배열 자료형은 초기화 시 특정 사이즈에 동일한 값을 넣어서 초기화 하는 방법을 제공합니다.
var threeDoubles = Array(repeating: 0.0, count: 3)
//threeDoubles 는 [Double] 자료형에 [0.0, 0.0, 0.0] 입니다.
// 번역 : 이런 형태도 가능합니다.
var threeDoubles = [Double](repeating: 0.0, count: 3)
// threeDoubles 는 [Double] 자료형에 [0.0, 0.0, 0.0] 입니다.
동일한 두 자료형의 배열을 +
연산자를 통해서 두 배열이 합쳐진 새로운 배열을 생성할 수 있습니다.
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles 는 [Double] 자료형이며, [0.0, 0.0, 0.0, 2.5, 2.5, 2.5,] 입니다.
리터럴을 통해서도 배열을 만들 수 있습니다. []
안에 원하는 원소를 넣고, ,
로 구분할 수 있습니다.
var shoppingList: [String] = ["Eggs", "Milk"]
// shoppingList 는 [String] 자료형에 Eggs 와 Milk 를 담고 있습니다.
이때 원소를 하나의 자료형으로 통일하면 배열의 자료형은 원소의 자료형으로 자동캐스팅이 됩니다.
var shoppingList = ["Eggs", "Milk"]
// shoppingList 는 [String] 자료형에 Eggs 와 Milk 를 담고 있습니다.
배열의 메소드와 프로퍼티, 인덱스 문법을 통해 배열에 접근하거나 수정할 수 있습니다.
배열의 개수는 count
프로퍼티를 사용할 수 있습니다.
print("shoppingList 는 \(shoppingList.count)개의 원소를 갖고 있습니다.")
// 출력 : shoppingList 는 2개의 원소를 갖고 있습니다.
배열이 빈 값인지 확인하고 싶다면 isEmpty
프로퍼티를 사용할 수 있습니다. isEmpty
는 count == 0
과 동일합니다.
if shoppingList.isEmpty{
print("쇼핑리스트는 비어있습니다.")
} else {
print("쇼핑 리스트는 비어있지 않습니다.")
}
// 출력 : 쇼핑 리스트는 비어있지 않습니다.
배열에 원소를 추가하고 싶다면 append(_:)
메소드를 사용할 수 있습니다.
* 번역 : 메소드나 함수에서 매개변수 앞에 _
가 있으면 호출할 때 매개변수 라벨을 생략할 수 있습니다.
shoppingList.append("Flour")
// shoppingList 는 이제 3개의 아이템이 되었고, 팬케이크를 구울 것 같네요 :)
혹은 이런 방법으로도 배열에 원소를 추가할 수 있습니다.
shoppingList += ["Baking Powder"]
// 쇼핑리스트는 이제 4개의 아이템을 갖고 있습니다.
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// 쇼핑리스트는 이제 7개의 아이템을 갖고 있습니다.
인덱스 문법을 활용하여 배열 안의 값을 확인하거나, 새로운 값을 할당할 수도 있습니다.
var firstItem = shoppingList[0]
shoppingList[0] = "Six eggs"
// firstItem 은 Eggs 가 되고,
// shoppingList[0] 는 "Six eggs" 가 됩니다.
// 번역 : 이때, 배열은 "값 타입" 이기 때문에, firstItem 이 Six eggs 로 변경되지 않습니다.
인덱스 문법을 활용할때는 사용하고자 하는 인덱스가 배열의 범위안에 있는지 확인하여야 합니다. 만약 배열에 포함되어 있지 않은 인덱스를 사용하면 런타임 에러가 발생할 것 입니다.
한편 다음과 같이 인덱스 문법을 활용하여 특정 범위의 배열에 접근하거나 수정할 수도 있습니다. 이때 배열은 배열 리터럴의 크기로 조정하여 기존의 값에 합쳐지게 됩니다.
shoppingList[4..6] = ["Bananas", "Apples"]
// shoppingList 는 이제 기존 3개 원소를 2개 원소로 줄여,
// 6개의 아이템을 갖게 됩니다.
배열 내 특정 인덱스에 삽입을 하고 싶다면 insert(_:at:)
메소드 를 사용할 수 있습니다.
shoppingList.insert("Maple Syrup", at 0)
// shoppingList 의 첫번째 원소에 Maple Syrup 이 들어가며 , 총 7개의 원소를 갖게 됩니다.
이와 유사하게 배열 내 특정 인덱스를 삭제하고 싶다면 remove(at:)
메소드를 사용할 수 있습니다. remove
메소드는 삭제한 원소를 리턴하는데, 필요없다면 무시하고 사용할 수 있습니다.
let mapleSyrup = shoppingList.remove(at: 0)
Note : 배열의 총 크기는 0
~ count - 1
입니다. 이때 count 가 0 이라면 빈 배열로, 접근할 수 없습니다. 인덱스를 활용한다면 이 점에 유의해서 사용해주세요.
배열 내 전체 값들을 for-in
반복문을 통해 접근할 수 있습니다.
for item in shoppingList {
print(item)
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas
반복 시 인덱스가 필요하다면 다음과 같은 형태도 고려해볼 수 있습니다.
for (index, value) in shoppingList.enumerated() {
print("Item\(index + 1) : \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas
세트는 비정렬 컬렉션에 같은 자료형의 값들을 고유
하게 저장하는 컬렉션을 의미합니다. 원소들의 순서가 중요하지 않거나, 원소가 반드시 1개만 존재해야 한다면 set
를 사용할 수 있습니다.
Note : Swift 의 set 자료형은 Foundation 의 NSSet class 를 따릅니다. 더 자세한 내용은 Bridging Between Set and NSSet 를 확인해주세요.
Set
에 저장되는 값들은 반드시 hashable, 즉 각 값들이 hash value 를 지녀 다른 값과 계산할 수 있는 자료형이어야 합니다. hash value 는 Int 값이며, 동일한 값은 동일한 hash value 를 갖고 있어야합니다.
번역 : Swift 4.2 이후로부터 hashable 이 적용된 자료형은 컴파일러에서 자체적으로 hash algorithm 이 적용됩니다. 이에 따라 보다 빠른 최적화가 필요하거나 쌉컴덕 이 아니라면 컴파일러에게 맡기고, 아무것도 안하면 됩니다 ! sturct 등은 해당 제네릭 자료형에 Hashable protocol, equatable protocl 을 적용하면 됩니다. 참고
Swift 내 기본 자료형들 (String, Int, Double, Bool 등) 은 자체적으로 hashable 를 내장하고 있으며 set value
나 dictionary key type
에 사용할 수 있습니다. Enumeration 값 역시 자체적으로 hashable 이 적용됩니다.
Set 은 Set<Element>
로 정의할 수 있으며, 배열과 달리 짧은 문법은 없습니다.
var letters = Set<Character>()
print(letters 는 Set<Character> 자료형으로, \(letters.count)개의 원소를 갖고 있습니다.)
// letters 는 Set<Character> 자료형으로 0개의 원소를 갖고 있습니다.
한번 Set 을 초기화하면 자료형이 정해지기에 자료형을 언급하지 않아도 됩니다. 다음과 같이 활용할 수 있습니다.
letters.insert("a")
letters = []
리터럴을 통해 Set 을 초기화할 수도 있습니다.
var favoriteGeners: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGeners 는 3개의 원소로 초기화되었습니다.
// 다음과 같이 짧게 표현할 수도 있습니다.
var favoriteGeners: Set = ["Rock", "Classical", "Hip hop"]
한번 선언되면, 해당 Set 는 같은 자료형만 삽입할 수 있게 됩니다.
다음 메소드와 프로퍼티를 통해 Set 에 접근하거나 수정할 수 있습니다.
세트의 개수는 count
프로퍼티를 사용할 수 있습니다.
print("저는 \(favoriteGenres.count)개의 음악 장르를 선호해요.")
// 출력 : 저는 3개의 음악 장르를 선호해요.
세트가 빈 값인지 확인하고 싶다면 isEmpty
프로퍼티를 사용할 수 있습니다. isEmpty
는 count == 0
과 동일합니다.
if favoriteGenres.isEmpty {
print("음악이 있기만 한다면, 난 까탈쟁이는 아니에요.")
} else {
print("전 특정 음악을 선호해요.")
}
// 출력: "전 특정 음악을 선호해요. "
세트에 원소를 삽입하고 싶다면 insert(_:at:)
메소드 를 사용할 수 있습니다.
favoriteGenres.insert("Jazz")
// favoriteGenres 는 이제 4개의 원소를 갖고 있습니다.
이와 유사하게 세트 내 특정 인덱스를 삭제하고 싶다면 remove(at:)
메소드를 사용할 수 있습니다. remove
메소드는 삭제한 원소를 리턴하는데, 필요없다면 무시하고 사용할 수 있습니다.
if let removeGenre = fvoriteGenres.remove("Rock") {
print("\(removeGenre)? 없앱니다 ~ ")
} els e{
print("그런 장르 좋아한 적 없습니다.")
}
// 출력 : "Rock"? 없앱니다 ~
너는 록을 좋아하지 않아
Note : 배열의 총 크기는 0
~ count - 1
입니다. 이때 count 가 0 이라면 빈 배열로, 접근할 수 없습니다. 인덱스를 활용한다면 이 점에 유의해서 사용해주세요.
한편 Set 가 특정 원소를 포함하고 있는지 확인하고 싶다면 contains(_:)
메소드를 사용할 수 있습니다.
if favoriteGenres.contains("Funk") {
print("하하 내 화려한 발재간을 보아라")
} else {
print("너무 시끄럽네 ..")
}
// 출력 : 너무 시끄럽네 ..
세트 내 전체 값들을 for-in
반복문을 통해 접근할 수 있습니다.
for genre in favoriteGenres {
print("\(genre)")
}
// Classical
// Jazz
// Hip hop
Swift 의 set 은 특정한 정렬이 없습니다. 혹시 특정한 기준으로 정렬된 set 를 반복하고 싶다면 sorted()
메소드를 활용해보세요.
for genre in favoriteGenres.sorted() {
print("\(genre)")
}
// Classical
// Hip hop
// Jazz
Set 자료형은 set 의 구조를 작업하는 효율적인 메소드들을 제공합니다. 합집합, 교집합, 차집합, 대칭차집합 와 같은 내용입니다.
다음과 같은 작업을 수행할 수 있습니다.
intersection(_:)
메소드를 활용하여 교집합 set 을 얻을 수 있습니다.symmetricDifference(_:)
메소드를 활용하여 대칭차집합 set 을 얻을 수 있습니다.union(_:)
메소드를 활용하여 두 set 을 합칠 수 있습니다.subtracting(_:)
메소드를 활용하여 여집합 set 을 얻을 수 있습니다.코드로 표현하면 다음과 같습니다.
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(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]
다음 도표를 통해 Set 의 관계를 살펴볼 수 있습니다. Set a 는 Set b 를 포함하므로 a 는 b 의 상위집합입니다. 한편 b 는 a 의 부분집합이 됩니다. Set c 는 b 와 관련이 없는 별개의 집합이 됩니다.
==
연산자를 통해 두 Set 이 모두 동일한 원소들을 포함하는지 확인할 수 있습니다.isSubset(of:)
메소드를 통해 어떤 Set 이 다른 Set 의 부분집합인지 확인할 수 있습니다.isSuperset(of:)
메소드를 통해 어떤 Set 이 다른 Set 의 상위집합인지 확인할 수 있습니다.isStrictSubset(of:)
혹은 isStrictSuperset(of:)
메소드들을 통해 각각이 부분집합, 상위집합이되 동일한 집합이 아닌지 확인할 수 있습니다.isDisjoint(with:)
을 통해 두 Set 가 공통된 원소를 없는지 확인할 수 있습니다.코드로 표현하면 다음과 같습니다.
let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]
houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAnimals.isDisjoint(with: cityAnimals)
// true
Dictionary 는 키와 값이라는 한 쌍을 기준으로 같은 자료형을 저장하는 비정렬 컬렉션입니다. 각 값들은 고유한 키에 속하게 되며, 이를 통해 딕셔너리 내에 값들을 확인할 수 있게 됩니다. 비정렬 컬렉션이므로, 실제 사전과 같이 특정 값을 찾고 싶다면 키를 통해 확인할 수 있습니다.
Note: Swift 의 딕셔너리는 Foundation 의 NSDictionary class 를 따릅니다. 자세한 사항은 Bridging Between Dictionary and NSDictionary 에서 확인할 수 있습니다.
딕셔너리는 Dictionary<Key, Value>
의 형태로 선언되지만, [Key:Value]
로 짧게 표현할 수도 있습니다. 이때 Key 값은 Hashable protocol 을 따르는 자료형이어야 합니다.
var namesOfIntegers: [Int: String] = [:]
// namesOfIntegers 는 빈 [Int:String] 딕셔너리 입니다.
한번 딕셔너리를 초기화하면 자료형이 정해지기에 자료형을 언급하지 않아도 됩니다. 다음과 같이 활용할 수 있습니다.
namesOfIntegers[16] = "sixteen"
// namesOfIntegers 는 이제 1 쌍의 Key-Value 를 갖고 있습니다.
namesOfIntegers = [:]
// naemsOfIntegers 는 빈 딕셔너리입니다.
리터럴을 통해 딕셔너리를 초기화할 수도 있습니다.
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
// airports 는 2개의 키-쌍 값을 갖고 있습니다.
// 다음과 같이 짧게 표현할 수도 있습니다.
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
한번 선언되면, 해당 딕셔너리는 같은 자료형만 삽입할 수 있게 됩니다.
다음 메소드와 프로퍼티를 통해 딕셔너리에 접근하거나 수정할 수 있습니다.
딕셔너리의 개수는 count
프로퍼티를 사용할 수 있습니다.
print("공항 딕셔너리는 \(airports.count)개의 원소를 갖고 있습니다.")
// 출력: "공항 딕셔너리는 2개의 원소를 갖고 있습니다."
딕셔너리가 빈 값인지 확인하고 싶다면 isEmpty
프로퍼티를 사용할 수 있습니다. isEmpty
는 count == 0
과 동일합니다.
if airports.isEmpty {
print("공항 노선이 없습니다.")
} else {
print("공항 노선이 있습니다.")
}
// Prints "공항 노선이 없습니다."
딕셔너리에 원소를 삽입하고 싶다면 인덱스 문법을 사용할 수 있습니다.
airports["LHR"] = "London"
// 공항 딕셔너리는 이제 3개의 원소를 갖고 있습니다.
딕셔너리의 원소를 수정하고 싶을때도 인덱스 문법을 사용할 수 있습니다.
airports["LHR"] = "London Heathrow"
// "LHR" 의 값은 "London Heathrow" 로 변경되었습니다.
인덱스 문법 대신 updateValue(_:forKey:)
메소드를 통해 특정 키의 값을 수정할 수 있습니다. 이때 updateValue(_:forKey:)
메소드는 해당 키가 존재하지 않는다면 nil
을 존재한다면 이전의 value
를 리턴합니다.
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
print("DUB 키의 이전 값은 \(oldValue) 입니다.")
}
// 출력: "DUB 키의 이전 값은 Dublin 입니다."
인덱스 문법 역시 이와 유사한 표현을 사용할 수 있습니다. 단, updateValue(_:forKey:)
메소드와 달리 이전의 값이 있더라도 그 값을 리턴하지 않습니다.
if let airportName = airports["DUB"] {
print("공항 이름은 \(airportName) 입니다.")
} else {
print("해당 공항은 없습니다.")
}
// Prints "공항 이름은 Dublin Airport 입니다."
딕셔너리 내 특정 키를 삭제하고 싶다면 인덱스 문법을 활용하여 특정 키에 nil 을 할당하면 됩니다.
airports["APL"] = "Apple International"
// "Apple International" 는 실제 공항이 아니므로 삭제합니다.
airports["APL"] = nil
// APL 은 딕셔너리에서 삭제되었습니다.
이와 유사하게 remove(at:)
메소드를 사용할 수 있습니다. remove
메소드는 삭제한 원소를 리턴하는데, 필요없다면 무시하고 사용할 수 있습니다.
if let removedValue = airports.removeValue(forKey: "DUB") {
print("삭제된 공항의 이름은 \(removedValue) 입니다.")
} else {
print("공항 딕셔너리는 DUB 키에 값을 갖고 있지 않습니다.")
}
// 출력: "삭제된 공항의 이름은 \(removedValue) 입니다."
딕셔너리 내 전체 값들을 for-in
반복문을 통해 접근할 수 있습니다.
for (airportCode, airportName) in airports {
print("\(airportCode): \(airportName)")
}
// LHR: London Heathrow
// YYZ: Toronto Pearson
딕셔너리의 키나 벨류로 반복하고 싶다면 다음과 같이 사용할 수 있습니다.
for airportCode in airports.keys {
print("공항 코드: \(airportCode)")
}
// 공항 코드: LHR
// 공항 코드: YYZ
for airportName in airports.values {
print("공항 이름: \(airportName)")
}
// 공항 이름: London Heathrow
// 공항 이름: Toronto Pearson
혹시 API 에 사용하기 위해 딕셔너리의 키나 벨류가 필요하다면 Array instance
를 활용할 수 있습니다.
let airportCodes = [String](airports.keys)
// airportCodes 는 ["LHR", "YYZ"]
let airportNames = [String](airports.values)
// airportNames 는 ["London Heathrow", "Toronto Pearson"]