본문 바로가기

iOS

[iOS] 배열, 집합, 튜플 자료형

배열, 집합, 튜플 자료형

Index

1. 배열 (Array)

2. 집합 (Set)

3. 튜플 (Tuple)

4. 딕셔너리 (Dict)

5. 총정리







1. 배열 (Array)


배열은 많은 프로그래밍 언어가 공통적으로 제공하는 자료형이다.

스위프트에서 사용하는 배열의 특징을 정리해본다.

  • 배열에 저장할 아이템의 타입에는 제약이 없으나, 하나의 배열에 저장하는 아이템 타입은 모두 같아야한다.
  • 선언 시 배열에 저장할 아이템 타입을 명확히 정의해야한다.
  • 배열의 크기는 동적으로 확장할 수 있다.

스위프트에서 배열을 정의하는 방법은 두 가지로, 정적(Static) 방식과 동적(Dynamic) 방식으로 나눌 수 있다.

정적인 방식은 배열 선언시 처음부터 배열을 구성하는 아이템을 포함하여 정의하는 방식이다.

이 방식은 별도의 배열 선언이 필요 없다는 장점이 있다.

정적 방식으로 배열 정의시에는 아래와 같이 대괄호 내에 차례대로 아이템을 나열하여 정의한다.

var cities = ['seoul', 'New York', 'LA', 'Santiago']

여기서도 타입 추론에 의해 cities 는 문자열 아이템들을 가지는 배열이된다.

스위프트에서 배열의 길이를 구할 때에는 .count 를 사용한다.

var ex = ['1','2','3','4']

ex.count    // 배열의 길이는 4

또한 특정 원소의 index 값을 알아내기 위해서는 index(of:) 메서드를 이용한다.

var cities = ['Seoul', 'New York', 'LA', 'Santiago']

for city in cities: {
    let idx = cities.index(of: city)
    print("\(idx)번쨰 원소는 \(city)")
}

이번엔 배열을 동적으로 사용하는 방법에 대해 알아본다.

값을 할당하지 않은 빈 배열을 선언하고 초기화할 때는 두 가지 형식을 사용할 수 있으며 첫번쨰 방식은 아래와 같다.

Array <원소 타입> ()

스위프트에서 배열을 정의하는 객체는 Array이다.

또한 배열을 정의할 때에는 반드시 저장할 원소들의 자료형도 함께 명시해 주어야 한다.

이처럼 사용 시점에 <> 기호를 사용하여 배열 내부에서 사용할 아이템 타입을 지정하는 문법을 제네릭 (Generic) 이라고 한다.

문자열 원소들을 저장할 배열이라면 <String>

범용 클래스 객체를 저장할 배열이라면 <AnyObject>로 작성한다.

동적으로 배열을 정의할 때에는 선언과 초기화 과정이 차례대로 필요하다.

// 문자열 배열의 선언 및 초기화
var cities = Array<String>()

// 문자열 배열 선언
var cities : Array<String>

이렇게 선언된 배열은 아직 초기화되지 않아 메모리 공간을 할당받지 않은 상태이다.

따라서 이 배열에는 아직 아무것도 저장할 수 없다. 배열을 사용하기 위해서는 아래와 같이 초기화 과정을 통해 메모리 공간을 할당받아야 한다.

/// 배열 초기화
cities = Array()

배열을 정의하는 구조체 Array 뒤에 () 를 붙여 초기화한다.

배열 또한 일반 변수 혹은 상수들이 선언과 초기화를 동시에 하는 것 처럼 동시에 가능하다.

var cities : Array<String> = Array<String>()

음... 딱봐도 매우 별로다..

그래서 두번쨰 방식으로 배열을 정의하는 법을 꼭 알아야한다!

두번째 방식은 아래와 같다.

[원소 자료형]()

앞에서 사용했던 배열 구조체 Array나 자료형을 표현하는 <> 기호가 사용되지 않으며 단순히 대괄호 사이에 아이템 타입을 기재하는 것만으로도 배열을 정의할 수 있다.

// 배열 선언
var cities: [String]

// 배열 선언 및 초기화
var cities = [String]()


1.1. 배열 아이템 동적 추가

필요에 따라 배열의 아이템을 동적으로 할당하는 방법에 대해 알아본다.

배열에 동적으로 아이템을 추가할 때에는 메서드를 사용하는데, 대표적인 메서드는 아래 3가지이다.

  • append(_:)
  • insert(_:at:)
  • append(contentsOf:)

타 언어의 메서드들과 매우 유사하다.

append(_:) 메서드는 입력된 값을 배열의 제일 뒤에 추가한다.

일반적으로 배열에서 존재하지 않는 인덱스에 접근시 오류가 발생하므로 이 메서드는 원소를 추가하기 이전에 배열의 크기를 확장해야한다.

insert(_:at:) 메서드는 이름 그대로 원소를 원하는 위치에 추가하고자 할 때 사용한다.

메서드의 인자값 at: 뒤에 입력되는 정수값은 배열에서 원소가 추가될 인덱스의 위치를 의미한다.

해당 인덱스에 새로운 값이 추가되면 이를 기준으로하여 뒷부분들의 원소들은 한칸씩 자연스레 밀려나는 결과를 가져온다.

append(contentsOf:) 메서드는 append(_:) 와 같이 배열의 제일 끝부분에 원소를 추가하지만, 개별 원소가 아니라 여러개의 원소를 한꺼번에 추가 할 때 사용하는 메서드이다.

즉, append(contentsOf:) 메서드의 인자값은 항상 배열이어야 한다.

위 메서드들을 이용한 예제코드를 살펴본다.

var cities = [String]()     //[]

cities.append("Seoul")      //['Seoul']
cities.append("New York")   //['Seoul', 'New York']
cities.insert("Tokyo", at: 1)   //['Seoul', 'Tokyo', 'New York']
cities.append(contentsOf: ["Dubai", "Sydney"]) // ["Seoul", "Tokyo", "New York", "Dubai", "Sydney"]

배열을 동적으로 사용할때에는 반드시 인덱스에 접근하기 위한 공간이 미리 만들어져 있어야 한다!

또 이러한 일을 간편하게 해결하기 위해 코코아 터치 프레임워크에서 해당 기능을 자동화하는 기능을 제공하고 있다.

extension Array: RangeReplaceableCollection {
    public init(repeating repeatedValue: Element, count: Int)
}

init 메서드의 인자값을 살펴본다.

repeatedValue 는 배열의 크기만큼 생성된 인덱스 각각에 기본값을 넣어줄 인자이며 count 는 배열의 길이를 정해주는 인자이다.

var cities = [String](repeating: "None", count:3)

이와 같이 배열을 생성하면 배열의 인덱스가 count 만큼 미릴 정의되고, 각각의 기본값이 None 으로 입력된 배열이 만들어진다.


1.2. 범위 연산자를 이용한 인덱스 참조

배열의 인덱스를 지정하여 개별 아이템을 참조하는 방식도 있으나 범위 연산자를 이용하면 특정 범위의 인덱스에 해당하는 아이템 모두를 참조할 수 있다.

주로 일정 범위의 배열 아이템을 한꺼번에 일어 들일 때 사용한다.


var num = [1, 2, 3, 4, 5]

num[0...2]  // [1, 2, 3]
num[2...3]  // [3, 4]
num[1..<3]  // [2, 3]


2. 집합 (Set)


집합 자료형은 타 언어에서 제공하는 집합 자료형과 동일하다.

같은 타입의 서로 다른 값을 중복없이 저장하고자 할 때 사용하는 자료형이다.

집합은 배열과 매우 유사하지만, 배열을 사용하기에는 순서가 그다지 중요하지 않은 데이터이거나 중복 없이 한번만 저장되어야 하는 데이터들을 다룰 때 배열 대신 사용할 수 있는 자료형이다.

집합은 내부적으로 Hash 연산의 결과값을 이용하여 데이터를 저장하므로 집합에 저장할 데이터 타입은 해시 연산이 가능한 타입이어야 한다.

즉, 다시 말하여 집합에 저장할 데이터는 해시값을 계산하는 방법을 제공해야 한다는 의미다.

cf) Hash 연산이란?

해시 알고리즘은, 임의의 입력된 값을 고정 길이의 데이터 크기로 변환해주는 알고리즘이다.
해시를 이용하면 아무리 긴 데이터나 아무리 짧은 데이터라 할지라도 고정 길이의 데이터로 변환할 수 있다.

집합을 정의할 때에는 타입 어노테이션을 제외하고 배열과 동일하다

var genres: Set = ["Classic", "Rock", "Ballad"]

만일 위 선언문에서 Set 이라는 타입 어노테이션이 빠지면 컴파일러는 이를 배열로 만든다.

빈 집합을 정의할 때에는 다음과 같이 Set 객체를 이용하여 직접 정의할 수 있다.

Set <원소 타입> ()
var genres =Set<String>()

genres.insert("Classic")
genres.insert("Rock")
genres.insert("Ballad")

2.1. 집합의 동적 추가 및 삭제

집합에 원소를 추가할 때는 insert(_at:) 메서드를 이용한다.

이 메서드는 함께 전달된 인자값을 집합에 추가하지만, 중복 값이 들어올 경우 자료형의 특성상 중복을 제외하기 위해 아무 처리도 하지 않는다.

집합의 원소를 삭제할 때는 remove(_at:) 메서드를 사용한다.

메서드의 인자값에는 삭제하고자 하는 원소의 값이 사용된다.

만일, 삭제할 원소가 존재하지 않는다면 nil을 반환한다.


if let removedItem = genres.remove("Rock") {
    print("Completed")
} else {
    print("Fail")
}

또한 스위프트는 집합의 아이템 전체를 삭제할 수 있는 메서드 removedAll() 을 제공한다.

이 메서드는 인자값 없이 호출되며 해당 집합의 모든 아이템을 일괄 삭제한다.

genres.removeAll()

if genres.isEmpty() {
    print("모든 원소 삭제 완료")
} else {
    print("모든 원속 삭제 실패")
}

이외에도 스위프트는 집합에 특정 원소가 존재하는지의 여부를 쉽게 확인할 수 있는 contains(_:) 메서드를 제공한다.

이 메서드는 인자값으로 입력된 데이터를 사용하여 해당 집합 내 일치하는 아이템이 존재하는지 확인하여 Bool 자료형을 리턴한다.



2.2. 집합 연산


집합 자료형을 이용한 대표적인 연산 메서드에 대해 알아본다.

  • intersection(_:) : 메서드명 그대로 교집합을 의미한다, 양쪽 집합에서 공통되는 아이템만 선택하여 새로운 집합을 만든다.

  • symmetricDifference(_:) : 양쪽 집합 중 어느 한쪽에만 있는 원소들만 선택하여 집합을 만들어준다.

  • union(_:) : 합집합을 의미한다, 양 쪽 집합의 모든 원소를 가지는 집합을 만든다. (단, 중복은 한번만 추가한다.)

  • subtract(_:) : 차집합을 의미한다. 한 쪽 집합에서 존재하는 다른 쪽 집합의 원소들을 제외한 새로운 집합을 만든다

  • isSubset(of:) : 주어진 집합이 특정 집합의 부분집합인지 여부를 판단하여 논리값을 반환한다.

  • isSuperset(of:) : 주어진 집합이 특정 집합의 모든 원소를 포함하는지 판단하여 논리값을 반환한다.

  • isDisjoint(with:) : 두 집합 사이의 공통 원소가 없는지를 판단하여 논리값을 반환한다.


3. 튜플 (Tuple)


스위프트의 튜플은 파이썬의 튜플과 동일하다.

한가지 지정 타입의 원소만 저장할 수 있는 배열이나 딕셔너리와는 달리 여러가지 타입의 아이템을 저장할 수 있지만, 상수적 성격을 띠므로 더이상 값을 추가하거나 삭제하는 등의 변경이 불가합니다.

즉, 타입과 관계없이 다양하게 저장할 수 있으나 오직 최초에 선언된 상태의 원소만 상요할 수 있고 수정이나 삭제, 추가 등 변경이 불가능한 것이 튜플의 특징이라고 할 수 있다.

튜플은 대괄호를 사용하는 배열이나 집합과는 달리 소괄호 () 를 이용하여 선언한다.

튜플 또한 원소들을 인덱스 속성을 통해 참조할 수 있지만 배열의 인덱스와는 다소 차이가 있다.

property 로 제공되기 때문에 . 연산자를 통해 접근한다.

// ex
tupleValue.0    // 0번째 원소
tupleValue.1    // 1번째 원소
tupleValue.2    // 2번째 원소

튜플 역시 없는 인덱스를 참조하고자 할 때는 오류가 발생하며, 인덱스가 0부터 시작한다는 점은 배열과 동일하다.

정리

튜플은 배열처럼 인덱스를 사용하지만 배열과는 다르게 속성 형식으로 사용하므로 대괄호 [] 대신 . 연산자를 통해 접근한다, 또한 저장된 값을 수정할 수 있는 배열과는 달리 일단 값이 정해지면 추가, 수정, 삭제가 모두 불가능하다는 것도 차이점이다.

튜플은 서로 다른 타입들을 집단 자료형으로 구성할 수 있기 때문에 메서드나 함수 에서 잘 이용된다.

함수나 메서드에서 둘 이상의 값을 반환하려면 별도의 자료형 객체를 만들거나 배열 또는 딕셔너리를 만들어 담아야 하는데, 이때 튜플을 이용하면 바로 전달할 수 있다.

// 결과값으로 튜플을 반환하는 함수

func getTupleValue() -> (String, String, Int) {
    return ("t", "''v", 100)
}

// 함수가 반환하는 튜플을 튜플 상수로 바인딩
let (a,b,c) = getTupleValue()

위 예시코드 형태는 매우 자주 이용되니 꼭 꼭 익혀두자.



4. 딕셔너리 (Dict)

많은 언어에서 사용하는 그 딕셔너리다.

마치 사전에서 고유 단어와 그 의미가 연결된 것 처럼, 고유 키(Key)와 그에 대응하는 값(Value)의 쌍으로 데이터를 저장하는 자료형이다.

인덱스 대신 Key값을 사용한다는 것을 제외하고는 배열과 매우 유사하다.


4.1. 딕셔너리 선언과 초기화

딕셔너리 또한 배열과 마찬가지로, 처음부터 필요한 모든 데이터를 입력한 상태로 사용하는 경우는 드물다.

대부분 빈 딕셔너리를 선언하고 초기화한 다음 필요한 시기에 아이템을 추가하는 방식을 사용한다.

Dictionary <Key type, Value type> ()

딕셔너리를 선언할 때에는 Dictionary 구조체가 이용된다.

제네릭 표기를 통해 KeyValue 값을 지정한다.

//ex

Dictionary <String, Int> ()

Dictionary <String, String> ()

Dictionary <String, AnyObject>  ()

배열의 선언과 초기화 구문을 매우 간결하게 줄일 수 있던것 처럼 딕셔너리 또한 선언과 초기화를 간결하게 정리할 수 있다.

위 예시를 간결한 형식으로 수정해본다.

//ex

[String: Int] ()

[String: String] ()

[String, AnyObject] ()

4.2. 딕셔너리에 동적 원소 추가 및 삭제

예제를 통해 바로 살펴보도록 한다.


var newCapital = [String: String] ()
newCapital["KR"] = "Seoul"

위와 같이 딕셔너리 변수명 뒤 [] 대괄호에 key 를 대입하고 대입연산자로 value를 할당한다.

이번에는 메서드를 사용하여 동적으로 값을 할당해본다.

딕셔너리에 값을 할당하는데 사용되는 메서드는 updateValue(_:forKey:) 다.

이 메서드는 키가 있는지에 따라 수행하는 역할이 달라지는데, 기존에 저장된 키가 있으면 연결된 값을 수정하기도 하지만 새로운 키가 입력되면 원소를 추가하는 역할도 한다.


var newCapital = [String: String] ()

newCapital.updateValue("Seoul", forKey: "KR")

newCapital.updateValue("London", forKey: "EN")

첫번쨰 업데이트 구문에서는 KR 이라는 키로 값을 업데이트하는데, 이 키는 newCapital 이라는 딕셔너리에 정의되지 않은 키이므로 값이 새롭게 추가되며 nil을 리턴한다.

두번쨰 업데이트 구문 또한 정상적으로 값이 추가되며 nil을 리턴한다.

만일 updateValue의 인자로 이미 사용했던 키를 전달하면 값의 업데이트가 발생하며 그 결과로 이전에 저장되어 있던 value를 리턴한다.

딕셔너리에 저장된 아이템을 제거할 때는 두가지 방법을 사용할 수 있다.

하나는 key에 연결된 value에 직접 nil을 할당하는 하는 것이고, 또 다른 하나는 명시적으로 removeValue(forKey:) 메서드를 이용하는 것이다.






5. 총정리


배열 - 순서있는 데이터들을 저장할 때 사용하며 중복된 값 지정이 가능하다, 저장된 데이터는 인덱스로 관리된다.

집합 - 순서가 없는 데이터를 저장할 때 사용하며, 중복지정은 불가하다.

딕셔너리 - 순서없는 데이터를 키-값 형태로 저장할 떄 사용하며, 중복된 값을 저장할 수 있으나 중복된 키는 사용이 불가하다.

튜플 - iteration이 불가하며 서로 다른 자료형을 집단으로 저장할 수 있다.

'iOS' 카테고리의 다른 글

[iOS] 내 소개 어플리케이션  (0) 2020.04.14
[iOS] Web Browser Project  (0) 2020.04.12
[iOS] 반복문, 조건문, 제어 전달문  (0) 2020.04.11
[iOS] 변수와 상수, 자료형, 연산자  (0) 2020.04.11
[iOS] Music Player  (0) 2020.04.09