으니의 개발로그

[Swift] 데이터 타입 고급(2-1) - 컬렉션형(배열, 딕셔너리, 세트) 본문

Swift/책 정리

[Swift] 데이터 타입 고급(2-1) - 컬렉션형(배열, 딕셔너리, 세트)

아잉으니야 2021. 1. 8. 21:44

[Swift] 데이터 타입 고급(2-1) - 컬렉션형(배열, 딕셔너리, 세트)

이 글은 Swift 프로그래밍 책을 읽고 요약한 내용입니다

 

 

컬렉션 타입에는 배열(Array), 딕셔너리(Dictionary), 세트(Set) 등이 있음

 

1. 배열

배열 : 같은 타입의 데이터를 일렬로 나열한 후 순서대로 저장하는 형태의 컬렉션 타입

 

// 대괄호를 사용하여 배열임을 표현
var names1: Array<String> = ["seonho", "donghae", "hero", "jieun"]

// 위 선언과 정확히 동일한 표현임. [String]은 Array<String>의 축약 표현
var names2: [String] = ["seonho", "donghae", "hero", "jieun"]

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

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

 

  • 각기 다른 위치에 같은 값이 들어갈 수 있음

  • 각 요소에 인덱스를 통해 접근 가능. 인덱스는 0부터 시작

  • 잘못된 인덱스로 접근하려고 하면 익셉션 오류(Exception Error)가 발생

  • 맨 처음과 맨 마지막 요서는 firstlast 프로퍼티를 통해 가져올 수 있음

  • firstIndex(of:) 메서드를 사용하면 해당 요소의 인덱스를 알아낼 수 있음. 만약 중복된 요소가 있다면 제일 먼저 발견된 요소의 인덱스 반환

  • 맨 뒤에 요소를 추가하고 싶다면 append(_:) 메서드 사용

  • 중간에 요소를 삽입하고 싶다면 insert(_:at:) 메서드 사용

  • 요소를 삭제하고 싶다면 remove(_:) 메서드 사용. 메서드를 사용하면 해당 요소가 삭제된 후 반환됨

print(names2[2])
/* hero */
names2[2] = "dongwon"
print(names2[2])
/* dongwon */
print(names2[4])    // 인덱스의 범위를 벗어났기 때문에 오류가 발생

names2[4] = "chanwon"    // 인덱스의 범위를 벗어났기 때문에 오류가 발생
names2.append("chanwon")    // 마지막에 chanwon이 추가됨
names2.append(contentsOf: ["kai", "siwoomin"])  // 맨 마지막에 kai와 siwoomin이 추가됨
names2.insert("joohyuk", at: 2)     // 인덱스 2에 삽입
names2.insert(contentsOf: ["dindin", "ravi"], at: 5)    // 인덱스 5의 위치에 dindin과 ravi가 삽입됨

print(names2[4])
/* jieun */
print(names2.firstIndex(of: "seonho"))
/* 0 */
print(names2.firstIndex(of: "jeonghoon"))
/* nil */
print(names2.first)
/* seonho */
print(names2.last)
/* siwoomin */

let firstItem: String = names2.removeFirst()
let lastItem: String = names2.removeLast()
let indexZeroItem: String = names2.remove(at: 0)

print(firstItem)
/* seonho */
print(lastItem)
/* siwoomin */
print(indexZeroItem)
/* donghae */
print(names2[1 ... 3])
/* ["dongwon", "jieun", "dindin"] */
  • 맨 아래 줄의 names2[1 ... 3] 표현은 범위 연산자를 사용하여 names2 배열의 일부만 가져온 것. 코드처럼 읽기만 가능한 것이 아니라 names2[1 ... 3 ] = ["A", "B", "C"] 와 같이 범위에 맞게 요소를 바꾸는 것도 가능

 

 

2. 딕셔너리(Dictionary)

딕셔너리(Dictionary) : 요소들이 순서 없이 키와 값의 쌍으로 구성되는 컬렉션 타입

 

  • 딕셔너리에 저장되는 값은 항상 키와 쌍을 이루게 되는데, 딕셔너리 안에는 키가 하나이거나 여러 개일수 있음

  • 하나의 딕셔너리 안의 키는 같은 이름을 중복해서 사용할 수 없음. 즉, 딕셔너리에서 키는 값을 대변하는 유일한 식별자가 됨

  • 대괄호로 키와 값의 타입 이름의 쌍을 묶어 딕셔너리 타입임을 표현

// 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] = ["seonho" : 36, "hero": 31, "donghae": 36]

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

 

  • 각 값에 키로 접근할 수 있음
  • 키는 유일해야 하며, 값은 유일하지 않음
  • 배열과 다르게 딕셔너리 내부에 없는 키로 접근해도 오류가 발생하지 않고 nil 을 반환함
  • 특정 키에 해당하는 값을 제거하려면 removeValue(forKet:) 메서드를 사용. 키에 해당하는 값이 제거된 후 반환
print(numberForName["seonho"])
/* 36 */
print(numberForName["jieun"])
/* nil */

numberForName["seonho"] = 100
print(numberForName["seonho"])
/* 100 */

numberForName["max"] = 999      // max라는 키로 999라는 값을 추가해줌

print(numberForName.removeValue(forKey: "seonho"))
/* 36 */

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

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

 

 

3. 세트

세트(Set) : 같은 타입의 데이터를 순서 없이 하나의 묶음으로 저장하는 형태의 컬렉션 타입

 

  • 세트 내의 값은 모두 유일한 값, 즉 중복된 값이 존재하지 않음
  • 순서가 중요하지 않거나 각 요소가 유일한 값이어야 하는 경우에 사용
  • 세트의 요소로는 해시 가능한 값이 들어와야 함
var names1: Set<String> = Set<String>()  // 빈 세트 생성
var names2: Set<String> = []     // 빈 세트 생성

// Array와 마찬가지로 대괄호를 사용
var names: Set<String> = ["seonho", "donghae", "hero", "seonho"]

// 그렇기 때문에 타입 추론을 사용하게 되면 컴파일러는 Set가 아닌 Array로 타입을 저장
var numbers = [100, 200, 300]
print(type(of: numbers))
/* Array<Int> */

print(names.isEmpty)
/* false */
print(names.count)  // 중복된 값은 허용되지 않아 seonho는 1개만 남음
/* 3 */

 

  • 요소를 추가하고 싶다면 insert(_:) 메서드 사용
  • 요소를 삭제하고 싶다면 remove(_:) 메서드 사용, 메서드를 사용하면 해당 요소가 삭제된 후 반환
print(names.count)
/* 3 */
names.insert("ravi")
print(names.count)
/* 4 */

print(names.remove("donghae"))
/* donghae */
print(names.remove("dindin"))
/* nil */

 

  • 세트는 자신 내부의 값들이 모두 유일하므로, 집합관계를 표현하고자 할 때 유용하게 쓰일 수 있으며, 두 세트의 교집합, 합집합 등을 연산하기에 매우 용이함
  • sorted() 메서드를 통하여 정렬된 배열을 반환해줄 수 있음
let englishClassStudents: Set<String> = ["seonho", "donghae", "hero"]
let koreanClassStudents: Set<String> = ["ravi", "seonho", "hero", "dindin", "jeonghoon"]

// 교집합 {"seonho", "hero"}
let intersectSet: Set<String> = englishClassStudents.intersection(koreanClassStudents)

// 여집합의 합(배타적 논리합) {"donghae", "ravi", "dindin", "jeonghoon"}
let symetricDiffSet: Set<String> = englishClassStudents.symmetricDifference(koreanClassStudents)

// 합집합 {"seonho", "donghae", "hero", "ravi", "dindin", "jeonghoon"}
let unionSet: Set<String> = englishClassStudents.union(koreanClassStudents)

// 차집합 {"donghae"}
let subtractSet: Set<String> = englishClassStudents.subtracting(koreanClassStudents)

print(unionSet.sorted())
/* ["dindin", "donghae", "hero", "jeonghoon", "ravi", "seonho"] */

 

  • 세트는 포함 관계를 연산할 수 있는 메서드로 구현되어 있음
let 새: Set<String> = ["비둘기", "닭", "기러기"]
let 포유류: Set<String> = ["사자", "호랑이", "곰"]
let 동물: Set<String> = 새.union(포유류)  // 새와 포유류의 합집합

print(새.isDisjoint(with: 포유류))  // 서로 배타적인지
/* true */
print(새.isSubset(of: 동물))   // 새가 동물의 부분집합인가요?
/* true */
print(동물.isSuperset(of: 포유류))   // 동물은 포유류의 전체집합인가요?
/* true */
print(동물.isSuperset(of: 새))     // 동물은 새의 전체집합인가요?
/* true */