[스위프트]

4.4 컬렉션형

스위프트는 튜플 이외에도 많은 수의 데이터를 묶어서 저장하고 관리할 수 있는 컬렉션 타입을 제공한다. 컬렉션 타입에는 배열(Array), 딕셔너리(Dictionary), 세트(Set)등이 있다.

4.4.1 배열

배열은 같은 타입의 데이터를 일렬로 나열한 후 순서대로 저장하는 형태의 컬렉션 타입이다. 각기 다른 위치에 같은 값이 들어갈 수도 있다.

배열 타입을 선언해줄 방법은 다양하다. let 키워드를 사용해 상수로 선언하면 변경할 수 없는 배열이 되고, var 키워드를 사용해 변수로 선언해주면 변경 가능한 배열이 된다. 실제로 배열을 사용할 때는 Array라는 키워드의 타입 이름의 조합으로 사용한다. 또, 대괄호로 값을 묶어 Array 타입임을 표현할 수도 있다. 빈 배열은 이니셜라이저 또는 리터럴 문법을 통해 생성해줄 수 있는데 isEmpty 프로퍼티로 비어있는 배열인지 확인해 볼 수 있다. 그리고 배열에 몇 개의 요소가 존재하는지 알고 싶으면 count 프로퍼티를 확인하면 된다.

// 대괄호를 사용하여 배열임을 표현한다.
var names: Array<String> = ["lhj", "hyungjun", "limhj", "lhyungj"]

// 위 선언고 ㅏ정확히 동일한 표현이다. [String]은 Array<String>의 축약 표현이다.
var names: [String] = ["lhj", "hyungjun", "limhj", "lhyungj"]

var emptyArray: [Any] = [Any]()       // Any 데이터를 요소로 갖는 빈 배열을 생성한다.
var emptyArray: [Any] = Array<Any>()  // 위 선언과 정확히 같은 동작을 하는 코드이다.

// 배열의 타입을 정확히 명시해줬다면 [ ]만으로도 빈 배열을 생성할 수 있다.
var emptyArray: [Any] = []
print(emptyArray.isEmpty)    // true
print(name.count)            // 4

배열은 각 요소에 인덱스를 통해 접근할 수 있다. 인덱스는 0부터 시작한다. 잘못된 인덱스로 접근하려고 하면 인셉션 오류(Exception Error)가 발생한다. 또, 맨 처음과 맨 마지막 요소는 fistlast 프로퍼티를 통해 가져올 수 있다. firstIndex(of:) 메서드를 사용하면 해당 요소의 인덱스를 알아낼 수도 있다. 만약 중복된 요소가 있다면 제일 먼저 발견된 요소의 인덱스를 반환한다. 맨 뒤에 요소를 추가하고 싶다면 append(_:) 메서드를 사용한다.

중간에 요소를 삽입하고 싶다면 insert(:at:)** 메서드를 사용하면 된다. 요소를 삭제하고 싶다면 **remove(_:) 메서드를 사용하게 되는데, 메서드를 사용하면 해당 요소가 삭제된 후 반환된다.

print(names[2])                // hyungjun
names[2] = "limhyungjun"
print(names[2])               // limhyungjun
print(names[4])               // 인덱스의 범위를 벗어났기 때문에 오류가 발생한다.

names[4] = "hyungjun"         // 인덱스의 범위를 벗어났기 때문에 오류가 발생한다.
names.append("hyungjun")      // 맨 마지막 hyungjun이 추가된다.
names.append(contentsOf: ["hj", "lh"] // 맨 마지막에 hj, lh가 추가된다.
names.insert("jhl", at: 2)    // 인덱스 2에 삽입된다.
// 인덱스 5의 위치에 abc, cba가 삽입된다.
names.insert(contentsOf: ["abc", "cba"], at: 5)

print(names[4])                     // lhyungj
print(names.firstIndex(of: "lhj"))  // 0
print(names.firstIndex(of: "baob")) // nil
print(names.first)                  // lhj
print(names.last)                   // lh

let firstItem: String = names.removeFirst()
let lastItem: String = names.removeLast()
let iundexZeroItem: String = names.remove(at: 0)

print(firstItem)
print(lastItem)
print(indexZeroItem)
print(names[1 ... 3])

위 코드에서 맨 아래 줄의 names[1 … 3] 표현은 범위 연산자를 사용하여 names 배열의 일부만 가져온 것이다. 코드처럼 읽기만 가능한 것이 아니라 names[1 … 3] = [”A”, “B”, “C”]와 같이 범위에 맞게 요소를 바꾸는 것도 가능하다.

스위프트의 배열을 비롯한 컬렉션 타입을 활용할 때 서브스크립트(Subscript) 기능을 많이 사용한다.

4.4.2 딕셔너리

딕셔너리는 요소들이 순서 없이 키와 값의 쌍으로 구성되는 컬렉션 타입이다. 딕셔너리에 저장되는 값은 항상 키와 쌍을 이루게 되는데, 딕셔너리 앞에는 키가 하나이거나 여러 개일 수 있다. 단, 하나의 딕셔너리 안의 키는 같은 이름을 중복해서 사용할 수 없다. 쉽게 말해서 아래의 코드에서 “lhj”라는 키가 두 번 쓰일 수 없다는 뜻이다. 즉, 딕셔너리에서 키는 값을 대변하는 유일한 식별자가 되는 것이다.

딕셔너리는 Dictionary라는 키워드와 키의 타입과 값의 타입 이름의 조합으로 써준다. 대관호로 키와 값의 타입 이름의 쌍을 묶어 딕셔너리 타입임을 표현한다. let 키워드를 사용하여 상수로 선언하면 변경 불가능한 딕셔너리가 되고, var 키워드를 사용하여 변수로 선언해주면 변경 가능한 딕셔너리가 된다. 빈 딕셔너리는 이니셜라이저 도는 리터럴 문법을 통해 생성할 수 있다. isEmpty 프로퍼티를 통해 비어있는 딕셔너리인지 확인할 수 있다. 그리고 count 프로퍼티로 딕셔너리의 요소 개수를 확인할 수 있다.

// typealias를 통해 조금 더 단순하게 표현해 볼 수 있다.
typealias StringIntDictionary = [String: Int]

// 키는 String, 값은 Int 타입인 빈 딕셔너리를 생성한다.
var numberForName: Dictionary<String, Int> = Dictionary<String, Int>()

// 위 선언과 같은 표현이다. [String: Int]는 Dictionary<String, Int>의 축약 표현이다.
var numberForName: [String, Int] = [String: Int]()

// 위 코드와 같은 동작을 한다.
var numberForName: StringIntDictionary = StringIntDictionary()

// 딕셔너리와 키와 값 타입을 정확히 명시해줬다면 [:]만으로도 빈 딕셔너리를 생성할 수 있다.
var numberForName: [String, Int] = [:]

// 초기값을 주어 생성해줄 수도 있다.
var numberForName: [String: Int] = ["lhj": 24, "babo": 100, "bab": 300]

print(numberForName.isEmpty)  // false
print(numberForName.count)    // 3

딕셔너리는 각 값에 키로 접근할 수 있다. 딕셔너리 내부에서 키는 유일해야 하며, 값은 유일하지 않다. 딕셔너리는 배열과 다르게 딕셔너리 내부에 없는 키로 접근해도 오류가 발생하지 않는다. 다만 nil을 반환할 뿐이다. 특정 키에 해당하는 값을 제거하려면 removeValue(forKey: ) 메서드를 사용한다. 키에 해당하는 값이 제거된 후 반환된다.

print(numberForName["babo"])     // 100
print(numberForName["hyungjun"]) // nil

numberForName["babo"] = 150
print(numberForName.["babo"])    // 150

numberForName["max"] = 999       // max라는 키로 999라는 값은 추가한다.
print(numberForName.["max"])     // 999

print(numberForName.removeValue(forKey: "lhj")) // 24

// 위에서 lhj 키에 해당하는 값이 이미 삭제되었으므로 nil이 반환된다.
// 키에 해당하는 값이 없으면 기본값을 돌려주도록 할 수 있다.
print(numberForName.removeValue(forKey: "lhj"))

// lhj 키에 해당하는 값이 없으면 기본으로 0이 반환된다.
print(numberForName["lhj", default: 0]) // 0