컬렉션(Collection)
Swift의 컬렉션(Collection)은 여러 개의 값을 저장하고 관리하는 데이터 구조이다
컬렉션에는 배열(Array), 딕셔너리(Dictionary), 집합(Set) 세 가지 주요 유형이 있다
- Swift의 컬렉션은 다음과 같은 특성을 가진다
- 값을 여러 개 저장할 수 있다
- 값의 순서가 있는지 여부에 따라 분류된다
- 값을 중복 허용하는지 여부에 따라 분류된다
- 컬렉션은 Array, Dictionary, Set 세 가지 주요 유형이 있다
| 컬렉션 타입 | 순서 유지 | 중복 허용 | 설명 |
|---|
| Array | ✅ 유지 | ✅ 허용 | 순서가 있는 값들의 리스트 |
| Dictionary | ❌ 유지 안 함 | 키 중복 불가 | 키-값(Key-Value) 쌍으로 데이터 저장 |
| Set | ❌ 유지 안 함 | 허용 안 함 | 중복 없는 고유한 값들의 집합 |
배열(Array)
- 배열(Array)은 같은 타입의 값들을 순서대로 저장하는 컬렉션 타입이다
- 값은 인덱스(Index, 0부터 시작)를 이용하여 접근할 수 있다
배열 선언 방법
- Swift에서 배열을 선언하는 방법은 여러 가지가 있다
리터럴(Literal)로 배열 선언
var names = ["Royce", "Steve", "Alice", "Bob", "Charlie"]
var numbers = [1, 2, 3, 4, 5]
명시적으로 타입을 지정하여 배열 선언
var names: [String] = ["Royce", "Steve", "Alice", "Bob", "Charlie"]
var numbers: [Int] = [1, 2, 3, 4, 5]
- 배열 타입을 명확하게 지정할 수 있다(
[String], [Int])
빈 배열 선언
var emptyArray1: [String] = []
var emptyArray2 = [Int]()
var emptyArray3: Array<Double> = Array()
- 빈 배열을 생성할 때
[] 또는 Array<T>()를 사용 가능
기본값을 가지는 배열 생성
var fiveSteves = Array(repeating: "Steve", count: 5)
var fiveRoyces = [String](repeating: "Royce", count: 5)
Array(repeating:count:)를 사용하여 특정 값이 반복되는 배열 생성 가능
배열(Array) 요소(Element) 접근
배열 요소 접근 방법
- 배열의 요소에 접근하려면 인덱스(Index)를 사용한다
- 인덱스는 0부터 시작하며, 배열의 크기 -1 까지 존재한다
let names = ["Royce", "Steve", "Alice", "Bob", "Charlie"]
print(names[0])
print(names[1])
print(names[2])
배열에서 첫 번째 요소(.first)와 마지막 요소(.last) 가져오기
let names = ["Royce", "Steve", "Alice", "Bob", "Charlie"]
print(names.first ?? "No Name")
print(names.last ?? "No Name")
- 배열이 비어있을 경우
nil을 반환하므로 ?? 연산자로 기본값 설정 가능
배열에서 특정 요소의 첫 번째와 마지막 인덱스 찾기
firstIndex(of:) ➡️ 첫 번째 인덱스 찾기
let names = ["Alice", "Royce", "Steve", "Royce", "Bob"]
if let index = names.firstIndex(of: "Royce") {
print("첫 번째 'Royce'의 위치: \(index)")
} else {
print("'Royce'가 없습니다.")
}
lastIndex(of:) ➡️ 마지막 인덱스 찾기
if let index = names.lastIndex(of: "Royce") {
print("마지막 'Royce'의 위치: \(index)")
} else {
print("'Royce'가 없습니다.")
}
- 중복된 요소가 있으 경우
firstIndex(of:)는 첫 번째, lastIndex(of:)는 마지막 요소의 인덱스를 반환
배열의 인덱스 확인(.startIndex, .endIndex)
let names = ["Royce", "Steve", "Alice", "Bob", "Charlie"]
print(names.startIndex)
print(names.endIndex)
startIndex는 항상 0, endIndex는 마지막 인덱스보다 1 큰 값
배열(Array)의 요소 변경(Element Modification)
| 변경 방법 | 메서드 또는 연산자 | 설명 |
|---|
| 단일 요소 변경 | [index] = newValue | 특정 위치의 요소 변경 |
| 범위 내 여러 요소 변경 | replaceSubrange(_:with:) | 여러 요소를 한 번에 변경 |
| 첫 번째 요소 변경 | .first | 배열의 첫 번째 요소 변경 |
| 마지막 요소 변경 | .last | 배열의 마지막 요소 변경 |
| 특정 값의 위치 찾아 변경 | firstIndex(of:), lastIndex(of:) | 특정 값이 있는 위치를 찾아 변경 |
| 모든 요소 변경 | indices / map | 배열의 모든 요소 변경 |
| 요소 추가 | append(_:) | 배열의 끝에 요소 추가 |
| 요소 삽입 | insert(_:at:) | 특정 위치에 요소 추가 |
| 요소 삭제 | remove(at:), removeFirst(), removeLast(), removeAll() | 특정 요소 또는 전체 삭제 |
| 요소 정렬 | .sort(), .sorted() | 배열 요소 정렬(원본 변경 또는 새 배열 반환) |
| 요소 반전 | .reverse(), .reversed() | 배열 요소 순서 반전(원본 변경 또는 새 배열 반환) |
| 요소 섞기 | .suffle(), .shuffled() | 배열 요소 무작위 섞기(원본 변경 또는 새 배열 반환) |
| 배열 비교 | ==, !=, elementsEqual(_:) | 두 배열이 같은지 비교 |
| 배열 활용 | joined(), reduce(), filter(), map(), contains() | 배열을 변형하거나 활용하는 다양한 방법 |
| 인덱스와 값 동시에 활용 | .enumerated() | 배열의 인덱스와 값을 동시에 가져오기 |
배열 요소 변경
특정 위치의 요소 변경([index] = value)
- 배열의 특정 인덱스에 있는 요소를 변경하려면
[index] = value 문법을 사용한다
var names = ["Royce", "Steve", "Alice", "Bob", "Charlie"]
names[2] = "Eve"
print(names)
특정 범위의 요소 변경 (replaceSubrange)
var names = ["Royce", "Steve", "Alice", "Bob", "Charlie"]
names.replaceSubrange(1...2, with: ["Eve", "David"])
print(names)
배열 요소 추가
배열 끝에 요소 추가 (append(_:))
var names = ["Royce", "Steve"]
names.append("Alice")
print(names)
특정 위치에 요소 추가(insert(_:at:))
var names = ["Royce", "Steve", "Alice"]
names.insert("Charlie", at: 1)
print(names)
배열 요소 삭제
특정 위치의 요소 삭제(remove(at:))
var names = ["Royce", "Steve", "Alice", "Bob", "Charlie"]
names.remove(at: 2)
print(names)
모든 요소 삭제(removeAll())
var names = ["Royce", "Steve", "Alice"]
names.removeAll()
print(names)
배열 요소 정렬(.sort() vs .sorted())
| 정렬 방법 | 동작 방식 | 원본 변경 여부 |
|---|
.sort() | 배열을 정렬된 상태로 변경 | ✅ 원본 변경 |
sorted() | 정렬된 새 배열을 반환 | ❌ 원본 변경 없음 |
원본을 직접 정렬(.sort())
var numbers = [5, 2, 8, 3, 1]
numbers.sort()
print(numbers)
정렬된 새 배열 반환(.sorted())
let numbers = [5, 2, 8, 3, 1]
let sortedNumbers = numbers.sorted()
print(sortedNumbers)
print(numbers)
배열 요소 반전 (.reverse() vs .reversed())
| 반전 방법 | 동작 방식 | 원본 변경 여부 |
|---|
.reverse() | 배열을 반전된 상태로 변경 | ✅ 원본 변경 |
.reversed() | 반전된 새 배열을 반환 | ❌ 원본 변경 없음 |
원본을 직접 반전(.reverse())
var numbers = [1, 2, 3, 4, 5]
numbers.reverse()
print(numbers)
반전된 새 배열 반환(.reversed())
let numbers = [1, 2, 3, 4, 5]
let reversedNumbers = numbers.reversed()
print(Array(reversedNumbers))
배열 요소 무작위 섞기(.shuffle(), .shuffled())
| 섞기 방법 | 동작 방식 | 원본 변경 여부 |
| .shuffle() | 배열을 무작위 순서로 변경 | ✅ 원본 변경 |
| .shuffled() | 무작위로 섞인 새 배열을 반환 | ❌ 원본 변경 없음 |
원본을 직접 섞기(.shuffle())
var numbers = [1, 2, 3, 4, 5]
numbers.shuffle()
print(numbers)
무작위로 섞인 새 배열 반환(.shuffled())
let numbers = [1, 2, 3, 4, 5]
let shuffledNumbers = numbers.shuffled()
print(shuffledNumbers)
print(numbers)
배열 요소 비교
배열이 같은지 비교(==, !=)
let array1 = [1, 2, 3, 4, 5]
let array2 = [1, 2, 3, 4, 5]
let array3 = [1, 2, 3, 4]
print(array1 == array2)
print(array1 == array3)
print(array1 != array3)
- 배열이 같은지 비교할 때는
== 연산자를 사용한다
배열 요소 순서까지 동일한지 비교(elementsEqual(_:))
let names1 = ["Royce", "Steve", "Alice"]
let names2 = ["Royce", "Steve", "Alice"]
let names3 = ["Alice", "Royce", "Steve"]
print(names1.elementsEqual(names2))
print(names1.elementsEqual(names3))
- 배열의 모든 요소가 같은지 비교할 때
elementsEqual(_:)을 사용한다
특정 요소 포함 여부 확인(contains(_:))
let numbers = [10, 20, 30, 40, 50]
print(numbers.contains(30))
print(numbers.contains(100))
- 배열에 특정 요소가 포함되어 있는지 확인할 때
contains(_:)를 사용한다
배열 활용
배열을 문자열로 변환(joined())
let words = ["Swift", "is", "awesome"]
let sentence = words.joined(separator: " ")
print(sentence)
- 배열의 요소를 문자열로 변환할 때
joined(separator:)를 사용한다
배열의 요소를 합산(reduce(_:_:))
let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(0, +)
print(sum)
-reduce를 사용하면 배열의 요소를 누적하여 계산 가능하다
배열에 특정 요소만 필터링(filter(_:))
let numbers = [10, 20, 30, 40, 50]
let filtered = numbers.filter { $0 > 25 }
print(filtered)
filter(_:)를 사용하면 특정 조건을 만족하는 요소만 선택
배열 요소를 변형(map(_:))
let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 }
print(doubled)
map(_:)을 사용하면 배열의 각 요소를 변형하여 새로운 배열을 생성 가능
배열의 인덱스와 값 동시에 활용하기(.enumerated())
- 배열을 순회하면서 인덱스와 요소 값을 동시에 가져올 때
enumerated() 메서드를 사용한다
enumerated() 기본 사용법
let names = ["Royce", "Steve", "Alice", "Bob"]
for (index, name) in names.enumerated() {
print("\(index): \(name)")
}
0: Royce
1: Steve
2: Alice
3: Bob
- 배열의 인덱스(index)와 요소 값(name)을 동시에 가져올 수 있다
enumerated()를 활용한 요소 변경
- 배열을 순회하면서 특정 요소를 변경할 수도 있다
var numbers = [10, 20, 30, 40, 50]
for (index, value) in numbers.enumerated() {
numbers[index] = value * 2
}
print(numbers)
짝수 인덱스만 출력
let numbers = [5, 10, 15, 20, 25]
for (index, value) in numbers.enumerated() {
if index % 2 == 0 {
print("짝수 인덱스 \(index)의 값: \(value)")
}
}
짝수 인덱스 0의 값: 5
짝수 인덱스 2의 값: 15
짝수 인덱스 4의 값: 25
enumerated()와 filter() 함께 사용
- 배열에 특정 조건을 만족하는 요소만 선택하고 싶을 때
enumerated()와 filter()를 함께 사용할 수 있다
let numbers = [10, 21, 32, 43, 54]
let evenIndexNumbers = numbers.enumerated().filter { index, _ in index % 2 == 0 }
print(evenIndexNumbers)
enumerated()와 map() 함께 사용
map()을 사용하면 기존 배열을 변환하면서 인덱스 값을 함께 사용할 수 있다
let numbers = [10, 20, 30, 40]
let indexedNumbers = numbers.enumerated().map { index, value in
"Index \(index): \(value)"
}
print(indexedNumbers)
딕셔너리(Dictionary)
- 딕셔너리는 각 키(Key)마다 하나의 값(Value)을 저장하는 키-값(Key-Value) 기반의 컬렉션이다
- 배열(Array)과 달리 순서(Order)가 없으며, 키(Key)를 이용하여 빠르게 값을 찾을 수 있다
딕셔너리의 특징
- 키(Key)는 고유(unique)해야 한다(중복 불가능)
- 값(Value)은 중복 가능
- 키(Key)는 Hashable 타입이어야 한다
- 검색, 추가, 삭제가 매우 빠르다(Hash Table 구조 사용)
딕셔너리의 정식 문법과 단축 문법
정식 문법(Full Syntax)
var students: Dictionary<String, Int> = Dictionary<String, Int>()
var students: Dictionary<String, Int> = ["Royce": 90, "Steve": 85, "Alice": 95]
단축 문법(Shorthand Syntax)
var students: [String: Int] = [:]
var students = ["Royce": 90, "Steve": 85, "Alice": 95]
딕셔너리의 주요 기능
요소 접근([] 서브스크립트)
print(students["Royce"])
print(students["Bob"])
값 변경 및 추가 ([] 사용)
students["Steve"] = 88
students["Charlie"] = 80
print(students)
특정 키 제거 (removeValue(forKey:))
students.removeValue(forKey: "Steve")
print(students)
특정 키 존재 여부 확인 (contains)
print(students.keys.contains("Royce"))
print(students.keys.contains("Bob"))
기본값을 제공하는 접근 (default:)
let royceScore = students["Royce", default: 0]
let bobScore = students["Bob", default: 0]
모든 키/값 조회
print(students.keys)
print(students.values)
딕셔너리 크기 확인 (.count)
print(students.count)
딕셔너리가 비어 있는지 확인 (.isEmpty)
print(students.isEmpty)
랜덤 요소 가져오기 (.randomElement())
if let randomStudent = students.randomElement() {
print(randomStudent)
}
딕셔너리 정렬 (sorted())
let sortedByKey = students.sorted { $0.key < $1.key }
print(sortedByKey)
let sortedByValue = students.sorted { $0.value < $1.value }
print(sortedByValue)
딕셔너리 값 업데이트 (updateValue(forKey:))
students.updateValue(92, forKey: "Alice")
print(students)
딕셔너리 비교
let dict1 = ["Royce": 90, "Steve": 85, "Alice": 95]
let dict2 = ["Royce": 90, "Steve": 85, "Alice": 95]
let dict3 = ["Royce": 90, "Steve": 80, "Alice": 95]
print(dict1 == dict2)
print(dict1 == dict3)
print(dict1 != dict3)
print(dict1.keys == dict2.keys)
print(dict1.values == dict2.values)
print(dict1.keys == dict3.keys)
print(dict1.values == dict3.values)
딕셔너리의 중첩 사용(Nested Dictionary)
var students: [String: [String: Int]] = [
"Royce": ["Math": 90, "Science": 85],
"Steve": ["Math": 80, "Science": 88]
]
print(students["Royce"]?["Math"])
print(students["Steve"]?["Science"])
students["Royce"]?["Math"] = 95
print(students["Royce"]?["Math"])
students["Alice"] = ["Math": 92, "Science": 90]
print(students)
딕셔너리와 반복문(Iteration)
let studentScores = ["Royce": 90, "Steve": 85, "Alice": 95]
for (name, score) in studentScores {
print("\(name): \(score)")
}
Royce: 90
Steve: 85
Alice: 95
for name in studentScores.keys {
print(name)
}
Royce
Steve
Alice
for score in studentScores.values {
print(score)
}
90
85
95
enumerated()를 사용하여 키-값을 인덱스와 함께 조회
for (index, (name, score)) in studentScores.enumerated() {
print("\(index): \(name) - \(score)")
}
0: Royce - 90
1: Steve - 85
2: Alice - 95
for (student, subjects) in students {
print("\(student)의 성적:")
for (subject, score) in subjects {
print(" \(subject): \(score)")
}
}
Royce의 성적:
Math: 95
Science: 85
Steve의 성적:
Math: 80
Science: 88
딕셔너리와 배열의 검색 속도 차이
| 데이터 구조 | 검색 방식 | 시간 복잡도 |
|---|
| 배열(Array) | 모든 요소를 순차 검색 | O(n) (최악의 경우) |
| 딕셔너리(Dictionary) | 해시 테이블을 이용한 검색 | O(1) (평균적으로) |
딕셔너리에서 Hashable이 중요한 이유
protocol Hashable : Equatable {
func hash(into hasher: inout Hasher)
}
해시(Hash)와 값의 유일성 보장
struct Person: Hashable {
let name: String
let age: Int
func hash(into hasher: inout Hasher) {
hasher.combine(name)
hasher.combine(age)
}
}
집합(Set)
Set의 특징
- 순서가 없다 ➡️ 배열(Array)과 달리 순서가 보장되지 않는다
- 중복된 요소를 허용하지 않는다 ➡️ 같은 값이 여러 번 저장되지 않는다
- 빠른 검색 및 추가 / 삭제 ➡️ 평균적으로 O(1) 시간 복잡도 (Hash Table 기반)
Set 선언 방법 (정식 문법 vs 단축 문법)
정식 문법 (Full Syntax)
var numbers: Set<Int> = Set<Int>()
print(numbers)
var numbers: Set<Int> = Set([1, 2, 3, 3, 4, 5])
print(numbers)
단축 문법 (Shorthand Syntax)
var numbers: Set = [1, 2, 3, 3, 4, 5]
print(numbers)
var emptySet: Set<String> = []
print(emptySet)
Set의 주요 기능
요소 추가 (insert)
numbers.insert(6)
print(numbers)
요소 삭제 (remove)
numbers.remove(3)
print(numbers)
특정 요소 포함 여부 확인 (contains)
print(numbers.contains(2))
print(numbers.contains(10))
Set 크기 확인 (count)
print(numbers.count)
Set이 비어 있는지 확인 (isEmpty)
print(numbers.isEmpty)
랜덤 요소 가져오기 (randomElement())
print(numbers.randomElement() ?? "No element")
Set의 업데이트 (Update)
update(_:)를 사용한 값 추가 및 업데이트
var fruits: Set = ["Apple", "Banana", "Cherry"]
fruits.update(with: "Mango")
print(fruits)
fruits.update(with: "Banana")
print(fruits)
Set의 집합 연산
- Swift의 Set은 집합 연산(Set Operator)을 지원하며, 일반 연산(
union, intersection 등)과 form으로 시작하는 원본 변경 연산이 있다
두 개의 Set 간 연산
var setA: Set = [1, 2, 3, 4, 5]
var setB: Set = [3, 4, 5, 6, 7]
print(setA.union(setB))
print(setA.intersection(setB))
print(setA.subtracting(setB))
print(setA.symmetricDifference(setB))
var setA: Set = [1, 2, 3, 4, 5]
var setB: Set = [3, 4, 5, 6, 7]
setA.formUnion(setB)
print(setA)
setA = [1, 2, 3, 4, 5]
setA.formIntersection(setB)
print(setA)
setA = [1, 2, 3, 4, 5]
setA.formSubtracting(setB)
print(setA)
setA = [1, 2, 3, 4, 5]
setA.formSymmetricDifference(setB)
print(setA)
Set과 for-in 반복문
let numbers: Set = [1, 2, 3, 4, 5]
for num in numbers {
print(num)
}
1
3
5
2
4