본문 바로가기

정보

Hashable과 Equatable은 밀접한 관계

https://explorer89.tistory.com/274

 

해시란? Hash, Hashable

Hash란?데이터를 관리, 유지하는 자료구조로 데이터들을 해시 함수를 통해 key로 분류하고, 그 key에 따라 value를 저장하는 형태를 뜻합니다.즉, "Hash"는 데이터를 고유한 숫자 값으로 변환하는 과정

explorer89.tistory.com

 

1. Equatable이란?

Equatable은 "두 값이 같은지 비교할 수 있는 능력"을 제공하는 프로토콜입니다.
이 프로토콜을 채택한 타입은 "== 연산자"를 구현해야 합니다.

struct Item: Equatable {
    let id: Int
    let name: String
}

// `Equatable`을 채택하면 Swift가 자동으로 `==`를 구현해줌
let itemA = Item(id: 1, name: "Apple")
let itemB = Item(id: 1, name: "Apple")
let itemC = Item(id: 2, name: "Orange")

print(itemA == itemB) // true (id와 name이 모두 동일)
print(itemA == itemC) // false (id가 다름)

 

 

2. Hashable과 Equatable의 관계

Hashable은 데이터를 고유한 해시 값으로 변환하는 능력을 제공하지만, Equatable을 반드시 필요로 합니다.

  • 해시 값만으로 데이터의 고유성을 100% 보장할 수 없습니다.
    예를 들어, 두 다른 객체가 우연히 동일한 해시 값을 가질 수도 있습니다. => 이걸 해시 충돌이라고 함
  • 따라서, Hashable은 해시 값이 동일한 경우 == 연산을 통해 두 객체가 실제로 같은지 최종 확인합니다.

 

3. Swift 구조체의 특별한 기능

구조체는 기본적으로 값 타입이기 때문에, Equatable이나 Hashable을 쉽게 사용할 수 있도록 설계돼 있어요.

  • 만약 구조체가 모든 속성이 Equatable하거나 Hashable한 타입으로 구성되어 있다면, Swift는 자동으로 Equatable과 Hashable을 구현해줍니다.
struct Section: Decodable, Hashable {
    let id: Int
    let type: String
    let title: String
    let subtitle: String
    let items: [String]  
}

let sectionA = Section(id: 1, type: "Type1", title: "Title1", subtitle: "Subtitle1", items: ["Item1"])
let sectionB = Section(id: 1, type: "Type1", title: "Title1", subtitle: "Subtitle1", items: ["Item1"])

print(sectionA == sectionB) // true (자동으로 Equatable 동작)

 

4. 직접 Equatable을 선언하지 않아도 되는 이유

(1) Hashable만 채택해도 충분:

  • Hashable을 선언하면, Swift가 Equatable도 포함해서 자동으로 구현해줍니다.
  • 예를 들어, Hashable을 사용하기 위해 내부적으로 == 연산이 필요하므로 Swift가 자동으로 이를 제공합니다.

(2) struct의 값 타입 특성:

  • struct는 값 타입으로 동작하기 때문에, Swift는 값 타입의 모든 속성이 동일하면 자동으로 "같다"고 판단하도록 구현해요.
  • 따라서, 따로 Equatable을 선언하거나 ==를 구현하지 않아도 Swift가 이를 처리합니다.

 

🔴 만약 Hashable을 직접 구현한다면, Swift가 자동으로 제공하는 == 연산자나 hash(into:)를 사용할 수 없게 되므로, 이를 명시적으로 구현해야 합니다.

struct Item: Hashable {
    let id: Int
    let name: String

    // 직접 구현: 해시 값 계산
    func hash(into hasher: inout Hasher) {
        hasher.combine(id) // 특정 속성(id)만 해시 계산에 사용
    }

    // 직접 구현: 동등 비교
    static func == (lhs: Item, rhs: Item) -> Bool {
        return lhs.id == rhs.id // id만 비교 기준
    }
}

let itemA = Item(id: 1, name: "Apple")
let itemB = Item(id: 1, name: "Orange")

print(itemA == itemB) // true (id만 비교)
print(itemA.hashValue == itemB.hashValue) // true (id만 해시 값에 포함)

 

이 경우, Hashable을 제대로 작동하게 하려면:

  1. hash(into:) 메서드에서 어떤 속성을 해시 값 계산에 사용할지 명시해야 하고,
  2. == 연산자에서 동등 비교의 기준을 직접 정의해야 합니다.

'정보' 카테고리의 다른 글

SOLID 원칙이란?  (0) 2024.12.17
디자인 패턴 - 싱글톤 패턴  (0) 2024.12.17
해시란? Hash, Hashable  (1) 2024.12.15
Static Dispatch vs Dynamic Dispatch  (1) 2024.12.13
클래스와 구조체가 섞여 있을 때...  (0) 2024.12.13