유일한 값을 갖도록 해서 Dictionary의 키값 또는 Set요소가 될 수 있게 하는 프로토콜
두 객체나 인스턴스를 비교하는데 사용되는 프로토콜이다. Hashable프로토콜은 데이터 타입의 값을 해시코드나 해시값으로 변환한다.
Equatable 프로토콜을 상속한다.
Hashable을 알기 전에..
딕셔너리의 Key값은 Hashable해야한다.
Hash값을 갖는다 라는 뜻이다.. 그게 무엇이냐?
Hash함수
어떤 숫자/글 등을 input으로 사용해 어떤 알고리즘을 통해 고정된 길이의 숫자/글자이면서 유일한 값이 나오는 함수
어떤 알고리즘인지는 중요하지 않다. 해쉬값이라는 예는 아래와 같다.
예 ex).
"steve", "cooker", "ilon"가 어떤 함수를 거치게 되면
해쉬(Hash)함수⤵️
(hashvalue) 12345 34123 45123
이렇게 변환이 된다고 한다면 해시벨류 12345와 34123가 서로 다른 값이니. 서로 다른 Input값이라는것을 보장한다.
steve를 넣어야지만 12345가 나오고
cooker를 넣어야지만 34123가나온다 ... 다른 글자 or 숫자를 넣으면 절대 이값이 나올 수 없다.
결과적으로 Hash함수를 통해 결과값으로 통해 나온 값들을 hashvalue라고 한다.
이 값들을 HashTable로 저장해놓는다.
이 형식을 통해 Dictionary는 배열보다 빠른 indexing이 가능한데, 배열에서 contains하면 배열 요소의 0번째부터 n번째 인덱스까지 하나 하나 접근해나가며 확인을 한다.
딕셔너리에선 Hash값을 이용해 접근을한다. 키 값이 steve인 벨류값을 찾기위해 딕셔너리에 순차적으로 하나 하나 접근을 하는게 아닌
해쉬함수를 통해 알게된 12345주소에 바로 접근을 한다. 그러므로 순차접근이 필요없이 한번에 steve의 value값에 접근할 수 있다.
이것을 Hashable하다, 해쉬가 가능하다 라고 한다.
결과적으로 어떤 타입이 Hashable하다는 것은 해시함수에서 input으로 쓰일 수 있는 "타입" 이라는 것이다.(Hashable)하다
스위프트의 기본 타입은 모두 Hashable하다. 그래서 Hash함수에 input으로 쓰일 수 있는데, 개발자는 스위프트의 기본 타입이 ✧ㅏ닌 개발자가 직접 만들어 사용하는 타입(Customtype)이 많다. 이런 타입들은 Hashable하지 않다.
⭐️⭐️⭐️
Hash함수에서 input으로 쓰일 수 없다. 하지만 Hashable프로토콜을 채택하고 구현한다면 커스텀 타입도 Hash함수에서 input으로 쓰일 수 있다.
결과적으로 Hashable을 채택하는 딕셔너리와 Set은 배열보다 빠른 검색을 가능케한다.
Hashable 프로토콜의 요구사항
func hash(into hasher: inout Hasher)
//Equatable프로토콜을 상속한다.
static func == (lhs: Some, rhs: Some) -> Bool
Hashable의 구현 원칙
- 구조체, 열거형의 경우 Hashable프로토콜 채택시 모든 저장속성(열거형은 모든 연관값)이 Hashable프로토콜을 채택한 타입이라면 hash(:into)메서드 자동 구현
예외
- 클래스는 인스턴스의 유일성을 갖게 하기 위해서는 hash(into:) 메서드 직접 구현해야함.
- 클래스는 원칙적으로 Hashable 지원 불가
- 열거형의 경우 연관값이 없다면 기본적으로 Equatable/Hashable하기 때문에 Hashable 프로토콜을 채택하지 않아도 됨
Hashable 프로토콜 구현 예제
struct Person: Hashable {
var name: String
var age: Int
func hash(into hasher: inout Hasher) {
hasher.combine(name)
hasher.combine(age)
}
}
이렇게 하면 Person타입의 인스턴스를 Set이나 Dictionary의 키로 사용할 수 있다.