본문 바로가기
Swift

SwiftUI: Hashable, Equatable

by songmoro 2023. 9. 23.
728x90

Hashable: A type that can be hashed into a Hasher to produce an integer hash value.(정수 해시 값을 생성하기 위해 해셔로 해시할 수 있는 유형.)

protocol Hashable : Equatable

 

Hashable은 값을 Hasher 역할을 하는 해시 함수에 공급할 수 있도록 하는 프로토콜입니다.

 

해시란 임의의 길이를 가진 데이터를 고정된 길이의 데이터로 매핑한 값을 의미하고, 해시 함수는 이 해시 값을 매핑 시켜주는 역할을 해요.

해시는 해시 값을 Index로 사용하여 해시 테이블에 접근할 수 있어 값을 빠른 속도( O(1) )로 찾을 수 있습니다.

 

기본 라이브러리의 String, Interger, Float, Boolean, Set은 기본적으로 Hashable을 지원하고, 사용자 정의 타입 또한 Hashable 하게 만들 수 있어요.

 


Hashable 채택

Set, Dictionary에서 사용자 정의 타입을 Key로 사용하고 싶다면 Hashable을 준수해야 합니다.

또한, Hashable은 Equatable을 상속 받기 때문에 Equatable의 요구 사항을 만족시켜야 돼요.

 

다음의 조건을 충족시킬 경우 컴파일러는 사용자 정의 타입의 Hashable를 자동으로 합성(synthesized) 시켜줍니다.

  • 구조체: 모든 저장된 속성들이 Hashable 할 때
  • 열거형: 모든 associated value가 Hashable 할 때, (associated value가 없는 열거형은 Hashable을 준수할 수 있습니다)
    • associated value(연관 값)은 raw value를 대체하는 값
enum A {
    case b = c
}
enum D {
    case e(f: Int)
}
// b: raw value
// e: associated value

 

타입의 Hashable 적합성(conformance)를 사용자 정의 하거나, 위에 나열한 조건들을 충족하지 않은 타입에 Hashable을 채택하거나, Hashable을 준수하도록 기존 타입을 확장(extension) 하기 위해선 hash(into:), Equtable의 == 메소드를 구현하면 됩니다.

func hash(into hasher: inout Hasher)

 

다행히도 hash 메소드는 combine(_:)로 간단하게 구현할 수 있습니다.

 

예를 들어서 아래의 구조체를 Hashable 하게 만든다면

struct GridPoint {
    var x: Int
    var y: Int
}

 

이런 식으로 extension을 추가할 수 있어요.

(물론 GridPoint는 Int가 이미 Hashable하기 때문에 자동적으로 Hashable 합니다.)

extension GridPoint: Hashable {
    static func == (lhs: GridPoint, rhs: GridPoint) -> Bool {
        return lhs.x == rhs.x && lhs.y == rhs.y
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(x)
        hasher.combine(y)
    }
}

Equatable을 채택하는 이유?(주관)

(여기서부턴 해시, 해시 테이블, 해시 충돌에 대한 사전 지식이 조금 필요합니다.)

해싱은 특정 임의 길이의 데이터(Key)를 그 데이터를 상징하는 고정 길이의 데이터(Bucket)로 변환하는 과정입니다.

해싱의 특성 상 입력은 무한할 수 있지만, 그를 위한 해시 테이블(Bucket 모음)은 유한합니다.

따라서 동일한 상징(Hash Value)을 갖는 Key가 필연적으로 존재할 수밖에 없고 이를 해시 충돌이라고 합니다.

 

예를 들어 “John Smith”, “Lisa Smith”, “Sandra Dee”, “John Doe”라는 데이터들이 있을 때, 해시 테이블의 크기가 3이라고 가정해볼게요.

 

4개의 데이터들을 순차적으로 넣어 보겠습니다.

  1. “John Smith” → 1번 Bucket
  2. “Lisa Smith” → 2번 Bucket
  3. “Sandra Dee” → 3번 Bucket
  4. “John Doe” → 3번 Bucket→ 해시 충돌 발생 → Chaining → 3번 Bucket

 

이렇게 된다면 “John Smith”, “John Doe” 두 데이터는 다른 데이터지만 동일한 해시 값을 갖게 되고, 결과적으로 아래와 같아져요.

  • “John Smith” == “John Doe” → false
  • “John Smith”.hashValue == “John Doe”.hashValue → true

 

다시 GridPoint를 가져와 생각해 보면 AGridPoint와 BGridPoint가 정말로 동일한 값인지 각 요소들을 검사하여 확인하기 위해 Equatable을 채택한다고 생각해요.

struct GridPoint {
    var x: Int
    var y: Int
}
let AGridPoint = GridPoint(x: 0, y: 1)
let AGridPoint = GridPoint(x: 0, y: 1)

print(AGridPoint == BGridPoint)
// (AGridPoint == BGridPoint) ->
// -> (AGridPoint.x == BGridPoint.x && AGridPoint.y == BGridPoint.y) ->
// -> true

 

 


참고

Swift) Hashable에 대해 알아보자

[Swift] Hashable 해야 한다? 해쉬값이란? (간단 요약)

Hashable이 무엇이고, Equatable을 왜 상속해야 하는지 설명하시오.

Swift ) Hashable

Hasher

Equatable, Hashable, and Comparable

What is the use of Hashable and Equatable in Swift? When to use which?

[IT 기술면접 준비자료] 해시(Hash)와 해시충돌(Hash Collision)

728x90

'Swift' 카테고리의 다른 글

Swift: Key Value Observing, KVO  (1) 2023.09.24
SwiftUI: String Subscript 접근  (0) 2023.09.23
SwiftUI: Codable, Encodable, Decodable  (0) 2023.09.15
SwiftUI: Optional  (0) 2023.09.15
UIKit: Foundation  (0) 2023.09.12