Hashable?

  • 정수 해시 값을 제공하고 Dictionary의 키가 될 수 있는 타입입니다.
    (Dictionary의 key value는 Hashable 프로토콜을 만족하는 타입이여야 합니다.)
  • 다르게 말하면 value가 hashable해야할 때, Hashable 프로토콜을 채택해서 구현해주면 hashable이 보장된 정수 해시 값을 제공합니다.
  • 구조체(Strucrue)의 저장프로퍼티는 모두 Hashable을 준수해야합니다.
  • 열거형(Enumeration)의 모든 연관값(associated values)은 모두 Hashable을 준수해야합니다.

Hashable이 필요한 경우?

Dictionary의 key에 들어갈 타입을 기본 데이터로 정해주면 Hashable을 만족하지 않는다는 에러는 만나지 않습니다. 왜냐하면 String,Int,Bool 기본 데이터 타입들은 모두 Hashable protocol을 만족하고 있기 때문이죠! 그런데 커스텀 타입을 key의 타입으로 정하면 에러를 만날 수 있습니다. 아래에 예제 코드로 설명해볼께요.

에러가 나는 예제 코드

// 에러가 나는 코드

class Participant{
 private var name : String
 private var age : Int
 
 init(_ name: String, _ age: Int){
 self.name = name
 self.age = age
 }
}

class Student: Participant{
  private var admissionYear : Int 
}

enum SpecializedField {
  case Math, ComputerScience
}

class Professor: Participant{
  private var specializedField: SpecializedField
}

class Lecture{
 private var participantList = [Participant:Bool]() // 딕셔너리
///... 이하 생략
}

만약 이렇게만 한다면


이런 에러를 만날 수 있습니다. 😢

에러 메세지를 보면 Dictionary에서 key의 타입으로 설정해준 Participant가 Hashable하지 않다고 나옵니다. 그렇다면 이제 Participant를 Hashable하도록 만들어주면 됩니다!

정상적으로 돌아가는 예제 코드


class Participant{
 private var name : String
 private var age : Int
 
 init(_ name: String, _ age: Int){
 self.name = name
 self.age = age
 }
}

extension Participant : Hashable{
    static func == (lhs: Participant, rhs: Participant) -> Bool {
        return lhs.name == rhs.name && lhs.age == rhs.age 
    }
    
    //* 여기
    func hash(into hasher: inout Hasher) {
       hasher.combine(name)
       hasher.combine(age)
    }
}

enum SpecializedField {
  case Math, ComputerScience
}

class Professor: Participant{
  private var specializedField: SpecializedField
}
extension Professor : Hashable{
    static func == (lhs: Professor, rhs: Professor) -> Bool {
        return lhs.specializedField == rhs.specializedField 
    }
    
    //* 여기
    func hash(into hasher: inout Hasher) {
       hasher.combine(specializedField)
    }
}

class Student : Participant{
  private var admissionYear : Int 
}

extension Student : Hashable{
    static func == (lhs: Student, rhs: Student) -> Bool {
        return lhs.admissionYear == rhs.admissionYear 
    }
    
    //* 여기
    func hash(into hasher: inout Hasher) {
       hasher.combine(admissionYear)
    }
}

class Lecture{
 private var participantList = [Participant:Bool]() // 딕셔너리
///... 이하 생략
}
  

방법은, 관련된 모든 클래스에서 Hashable을 구현해주면 됩니다. 관련 클래스의 모든 저장 프로퍼티를 Hashable하도록 만들어주면 돼요!

그럼 방법을 좀 더 살펴보죠.

Apple Developer Document에 있는 Dictionary에 들어가보니까 해쉬값을 제공하기 위한 인스턴스 메소드로

func hash(into hasher: inout Hasher)

가 있네요. Hashable을 준수하려면 hash(into:)가 필수라고 되어있습니다.

파라미터로 Hasher타입의 hasher를 받고있죠? 문서에는 필수 구성요소들에게 주어진 hasher를 공급함으로써 value의 필수 구성요소들을 해쉬하라고 하네요! (Hashes the essential components of this value by feeding them into the given hasher.)

이 일은 위에 정상적으로 돌아가는 예제 코드에서 extension으로 Hashable을 채택한 부분에 있는 hash(into:) 메소드('* 여기'표시한 곳 )가 해주는 일 같아요!

그럼 여기에 나오는 hasher는 뭘까요?

Hasher?

hasher는 구조체로, 해당 인스턴스의 구성요소를 결합할 때 사용한다고 해요.(The hasher to use when combining the components of this instance.)

그럼 여기에 나오는 combine은 뭘까요?

combine?

제네릭 인스턴스 메소드로 Hasher 구조체에서 value를 추가하는 메소드인데요.

mutating func combine<H>(_ value: H) where H : Hashable

해셔에 주어진 값을 추가하여 그 필수적인 부분을 해셔 상태로 혼합한다고 해요.(Adds the given value to this hasher, mixing its essential parts into the hasher state.)

이 일은 위에 정상적으로 돌아가는 예제 코드에서 '* 여기'표시한 곳 안에 haser.combine() 이 부분에서 해주는 일 같네요.

제 생각에는 단순히 저장 프로퍼티만 hash 값으로 만들어 주면 하나의 객체를 이루는 요소임을 알 수 없으니까 프로퍼티들을 묶어주는 것 같아요. (혹시 아시는 분 있다면 댓글 남겨주세요!)

이렇게 participantList에 key로 들어갈 수 있는 커스텀 타입의 모든 저장 프로퍼티를 Hashable 하도록 만들어주면 Participant 타입을 딕셔너리의 Key 타입으로 쓸 수 있게 됩니다!

참고한 자료

  1. How to conform to the Hashable protocol - Swift version: 5.1
  2. Dictionary - Apple Developer Document
  3. Hashable - Apple Developer Document
  4. [hash(into:) - Apple Developer Document] (https://developer.apple.com/documentation/swift/dictionary/2995338-hash)
  5. hasher - Apple Developer Document
  6. combine(_:) - Apple Developer Document

궁금한 점, 틀린 내용, 오타 지적, 오역 지적 등 피드백 환영합니다! 댓글로 남겨주세요!
😊 🙏

profile
Swift, iOS 앱 개발 공부하고 있어요!

5개의 댓글

comment-user-thumbnail
2020년 3월 14일

자바의 hashCode의 동작이랑 유사하네요~
자바의 hashCode가 더 보기엔 좋은 느낌입니다 ㅋㅋㅋ

자바의 hashCode는 공통 상위 클래스인 Object 클래스에 정의되어 있어서 모든 클래스는 hashCode를 이미 내장하고 있어요!
그래서 hashCode는 이미 있고, 이를 재정의 하는 경우는 어떤 클래스의 객체가 같은 경우를 개발자가 직접 정의해주고 싶을 경우에 재정의 한답니다!

1개의 답글
comment-user-thumbnail
2020년 3월 14일

오 정리 잘해주셔서 해셔블에 대해 더 이해하게 된거같아요! 잘읽고 갑니다 !! 🌷

1개의 답글