Introduction
: 스위프트의 주요 Collection type은 Array, Set, Dictionary가 있다.
Array는 ordered collection, Set은 Unordered, unique value collection이고 Dictionary는 Unordered, key-value 기반 collection이다.
Mutability Of Collections
: Mutability를 결정하는 기준은 collection을 var, let 중 무엇으로 선언했느냐에 따른다.
var로 선언하면 mutable이 되고, collection이 포함하고 있는 item들을 adding, removing, changing할 수 있다.
let으로 선언하면 immutable이 되고, item들의 조작이 불가능하다.
*변경이 불가능한 collection에 대해 모두 immutable로 선언하는 것이 예상치 못한 side effect나 performance 측면에서도 유리하다.
Arrays
: 같은 type의 value들을 저장한다. 똑같은 값의 중복 저장이 가능하다.
: Objective-C의 NSArray와 bridged(호환)된다.
Creating an Empty Array
var someInts = [Int]()
print ("someInts is of type [Int] with \(someInts.count) items.")
// Prints "someInts is of type [Int] with 0 items."
// someInts는 [Int] 타입이 된다. Int 타입의 값들을 저장할 수 있는 배열이다.
Array가 이미 타입이 지정되었으면 타입을 명시할 필요없이 바로 'empty array literal'로 빈 배열을 생성할 수 있다.
someInts.append(3)
// someInts는 1개의 Int 타입의 value를 저장하고 있다.
someInts = []
// someInts는 empty array가 된다. 하지만 여전히 [Int] 타입이다.
Creating an Array with a Default Value
: Array 초기화시 size와 default값을 지정할 수 있다.
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles 타입은 [Double]이고, [0.0, 0.0, 0.0] 값을 갖는다.
Creating an Array by Adding Two Arrays Together
: 기존 두개의 Array를 합쳐서 하나의 Array를 생성할 수 있다. 단, 기존 두개의 Array의 타입이 compatible해야 한다.(compatible이란 의미가, 꼭 같은 타입만을 의미하는것은 아닌것 같다. 상속 관계에 있는 클래스로 시도를 해봤으나 실패했다. Double, Int 타입도 실패했다. 하지만 same이 아닌 compatible이란 단어를 쓴것으로 보아 같은 타입이 아님에도 '+' 연산이 되는 type 메커니즘이 있는게 아닐까?)
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles는 [Double]타입이다.
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles는 [Double]타입으로 추론되고, [0.0, 0.0, 0.0, 2.5, 2.5, 2.5] 값을 갖는다.
Creating an Array with an Array Literal
: Array literal로 Array선언이 가능하다.
var shoppingList: [String] = ["Eggs", "Milk"]
shoppintList는 [String] 타입으로 선언되었기 때문에 array literal 안에는 String 타입의 value들이 들어가야 한다.
단, 타입을 명시하지 않을 수도 있다.
var shoppingList = ["Eggs", "Milk"]
타입을 명시하지 않았지만 array literal에 들어있는 값들이 모두 String 타입이기 때문에, [String] 타입으로 추론된다.
Accessing and Modifying an Array
: array의 값을 접근하거나 변경하기 위해서 array의 메서드나 프로퍼티 또는 subscript syntax를 이용한다.
print("The shopping list contains \(shoppingList.count) items")
if (shoppingList.isEmpty) {
print("The shopping list is empty")
} else {
print("The shopping list is not empty")
}
append(_:) 메서드를 이용해 item을 array 끝에 추가할 수 있다.
shoppingList.append("Flour")
+= 연산자를 이용해 한개 이상의 item들을 추가할 수 있다.
shoppingList += ["Banking powder"]
shoppingList +=["Chocolate spread", "Cheese", "Butter"]
subscript syntax를 이용해서 값을 검색할 수 있다.
var firstItem = shoppingList[0]
또한 subscript syntax를 이용해 기존 index에 값을 변경할 수 있다.
shoppingList[0] = "Six eggs"
subscript syntax에 범위를 지정해서 값을 변경할 수 도 있다. 심지어 변경하려는 값들의 범위와 subscript syntax로 지정한 범위가 맞지 않아도 된다.
//현재 shoppingList에는 ["Six Eggs", "Milk", "Flour", "Banking powder", "Chocolate Spread", "Cheese", "Butter"] 7개 item들이 들어있다.
shoppingList[4..6] = ["Bananas", "Apples"]
// 4, 5, 6번째 index item인 "Chocolate Spread", "Cheese", "Butter"이 "Bananas", "Apples"로 바뀌면서 shoppingList의 갯수도 7개에서 6개로 줄어들었다.
shoppingList.insert("Maple Syrup", at: 0)
// 첫번째 index item이 "Maple Syrup"이 되고 나머지 6개 item들은 하나씩 뒤로 밀린다. 총 갯수 7개.
let mapleSyrup = shoppingList.remove(at: 0)
// "Maple Syrup"이 제거 되고 다시 갯수는 6개가 된다.
let apples = shoppingList.removeLast()
// "Apples"가 제거 된다. array의 마지막 item을 제거할 때는 remove(at:) 보다 removeLast()를 사용해라. index가 array의 범위를 벗어나면 runtime error가 발생한다.
Iterating Over an Array
for item ins shoppingList {
print(item)
}
// Six eggs, Mils, Flour, Baking Powder, Bananas가 출력된다.
for (index, value) in shoppingList.enumerated() {
print("Item \(index + 1): \(value)")
}
// index가 필요한 경우 Tuple을 사용한 enumerate를 한다.
Sets
: Set은 Unordered, unique value collection이다. Objective-C의 NSSet과 bridged 된다.
Hash Values For Set Types
: Set에 저장되기 위해선 hashable type이어야 한다. 즉, hash value를 계산할 수 있는 type이어야 하고, a == b 이면, a.hashValue == b.hashValue 여야 한다.
Swift의 모든 basic type( String, Int, Double, Bool )들은 hashable 하다. Enumeration case value들도(associated value들 없이) 기본적으로 hashable 하다.
* 자신이 만든 custom type이 dictionary key나 set value type으로 저장되길 원한다면, Hashable 프로토콜을 conform해야 한다. hashable 프로토콜을 구현한다는 것은 hashValue를 property로 제공해야 한다는 것이다. 다만, hashValue는 꼭 프로그램내 또는 다른 프로그램에서 항상 같은 값을 가지고 있을 필요 없다.
Hashable 프로토콜은 Equatable 프로토콜을 conform하고 있으므로 equals operator(==)를 구현해야만 한다. '==' 구현은 다음 세가지 조건을 만족해야 한다.
1) a == a
2) a == b 는 b == a를 의미한다.
3) a == b 이고 b == c 이면 a == c 이다.
Set Types Syntax
: Set<Element> 일때, 'Element'는 해당 Set이 저장할 수 있는 Type 이라는 뜻이다.
Creating and Initializing an Empty Set
var letters = Set<Character>()
print("letters is of type Set<Character> with (letters.count) items.")
letters.insert("a")
letters = []
// letters는 empty set이 되지만, Set<Character> type을 유지한다.
Creating a Set with an Array Literal
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
// 이처럼 type을 생략할 수도 있다.
Accessing and Modifying a Set
print("I have \(favoriteGenres.count) favorite music genres.")
// Prints "I have 3 favorite music genres."
if favoriteGenres.isEmpty {
print("As far as music goes, I'm not picky.")
} else {
print("I have particular music preferences.")
}
favoriteGenres.insert("Jazz")
// 4 items을 포함하게 된다.
if let removedGenre = favoriteGenres.remove("Rock") {
print("\(removedGenre)? I'm over it.")
} else {
print("I never much cared for that.")
}
// Rock이 있기 때문에 Rock이 제거되고 "Rock? I'm over it."이 출력된다. 3 items을 포함하게 된다.
if favoriteGenres.contains("Funk") {
print("I get up on the good foot.")
} else {
print("It's too funky in here.")
}
// 해당 item이 포함되었는지 check하기 위해 contains() 메서드를 사용한다.
Iterating Over a Set
for genre in favoriteGenres {
print("\(genre)")
}
// Jazz, Hip hop, Classical을 출력.
Set은 unordering collection이기 때문에 ordered iterating을 하기 위해서는 sorted() 메서드를 사용해야 한다. sorted() 메서드는 '<' operator 기준으로 정렬시킨 array를 반환한다.
for genre in favoriteGenres.sorted() {
print("\(genre)")
}
// Classical, Hip hop, Jazz 순으로 출력된다.
Performing Set Operations
: set operation들을 통해서 교집합, 합집합, 여집합 등을 체크할 수 있다.
Fundamental Set Operations
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 Membership and Equality
- '==' operator는 두 set이 모두 같은 value들을 포함하고 있는지 체크한다.
- isSubset(of:), isSuperset(of:)를 통해 포함관계(equal도 포함)를 파악할 수 있다.
- isStrictSubset(of:), isStrictSuperset(of:)은 equal은 해당되지 않는 포함관계를 파악할 수 있다.
- isDisjoint(with:)를 통해 두 set이 서로소 인지 체크할 수 있다.
Dictionaries
: <key, value> 이면서 unordering collection이다. Objective-C의 NSDictionary 타입과 bridged 된다.
Dictionary Type Shorthand Syntax
: dictionary의 keyType은 hashable 프로토콜을 conform해야 한다. dictionary 타입은 '[Key:Value]'로 표현할 수 있다.
Creating an Empty Dictionary
var namesOfIntegers = [Int: String]()
namesOfIntegers[16] = "sixteen"
namesOfIntegers = [:]
// namesOfIntegers는 empty dictionary로 초기화 되었더라도 기존 type인 '[Int: String]'을 유지한다.
Creating a Dictionary with a Dictionary Literal
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
// 타입을 명시하지 않고 Dictionary 초기화가 가능하다.
Accessing and Modifying a Dictionary
print("The airports dictionary contains \(airports.count) items.")
// "The airports dictionary contains 2 items" 출력
if airports.isEmpty {
print("The airports dictionary is empty.")
} else {
print("The airports dictionary is not empty.")
}
airports["LHR"] = "London"
// airports는 이제 3 item을 포함한다.
airports["LHR"] = "London Heathrow"
// "London" 값을 "London Heathrow"로 대체된다.
위 subscript를 사용해서 value를 set하는 것을 updateValue(_:forKey:) 메서드로 대체할 수 있다. 다만, updateValue(_:forKey:) 메서드는 oldValue가 존재하면 return 한다.
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
print("The old value for DUB was \(oldValue).")
}
// "DUB" key에 "Dublin" 값이 저장되어 있기 때문에 oldValue에 "Dublin"이 return 된다.
// "The old value for DUB was Dublin" 출력.
if let airportName = airports["DUB"] {
print("The name of the airport is \(airportName).")
} else {
print("That airport is not in the airports dictionary.")
}
// "The name of the airport is Dublin Airport" 출력
airports["APL"] = "Apple International"
airports["APL"] = nil
// nil 값을 대입하면 해당 Key, Value는 Dictionary에서 제거 된다.
위 nil을 대입해서 해당 값을 제거하는것을 removeValue(forKey:) 메서드로 대체할 수 있다. 이 메서드는 해당 값이 있으면 제거하고 제거된 값을 return한다. 해당값이 없으면 nil을 return 한다.
if let removedValue = airports.removeValue(forKey: "DUB") {
print("The removed airport's name is \(removedValue).")
} else {
print("The airports dictionary does not contain a value for DUB.")
}
// "DUB" key에 대한 "Dublin Airport"이 removedValue에 대입된다.
// "The removed airport's name is Dublin Airport." 출력
Iterating Over a Dictionary
for (airportCode, airportName) in airports {
print("\(airportCode): \(airportName)")
}
// YYZ: Toronto Pearson, LHR: London Heathrow 출력.
for airportCode in airports.keys {
print("Airport code: \(airportCode)")
}
// key만 iterating이 가능하다.
for airportName in airports.values {
print("Airport name: \(airportName)")
}
// value만 iterating이 가능하다.
Dictionary의 keys, values를 통해 array를 초기화 할 수 있다.
let airportCodes = [String](airports.keys)
// airportCodes는 ["YYZ", "LHR"]로 초기화 된다.
let airportNames = [String](airports.values)
// airportNames는 ["Toronto Pearson", "London Heathrow"]로 초기화 된다.
Dictionary는 unordering collection이다. ordering iterating을 하려면 keys 또는 values의 sorted() 메서드로 iterating을 해야한다.