๐—ถ๐—ข๐—ฆ/๐Ÿ iOS ๊ฐœ๋…ํŽธ

[iOS / Swift] Equatable, Hashable, Comparable

z_ero 2025. 11. 5. 00:08

Swift์—๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ํ”„๋กœํ† ์ฝœ์ด ์กด์žฌํ•˜์ฃ ! 

๊ทธ์ค‘์—์„œ๋„ Equatable, Comparable, Hashable์€ ๊ฐ€์žฅ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ๋ณธ ํ”„๋กœํ† ์ฝœ์ž…๋‹ˆ๋‹ค.

๋ชจ๋ธ ๋น„๊ต, ์ •๋ ฌ, ์ค‘๋ณต ์ œ๊ฑฐ ๋“ฑ ์‹ค๋ฌด์—์„œ๋„ ์ž์ฃผ ๋“ฑ์žฅํ•˜๋Š” ์ค‘์š”ํ•œ ๊ฐœ๋…์ธ๋ฐ์š”.

 

๊ทธ๋Ÿผ ์ฐจ๊ทผ์ฐจ๊ทผ ํ•˜๋‚˜์”ฉ ์•Œ์•„๋ด…์‹œ๋‹ค!

๐ŸŽ Equatable

Equatable์€ ๋‘ ๊ฐ’์ด ๊ฐ™์€์ง€ ๋น„๊ตํ•  ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…์„ ์ •์˜ํ•˜๋Š” ํ”„๋กœํ† ์ฝœ์ž…๋‹ˆ๋‹ค.

๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด…

“Values of types that conform to Equatable can be checked for equality using the == operator.”

 

์ฆ‰, Equatable์„ ์ฑ„ํƒํ•˜๋ฉด == ์—ฐ์‚ฐ์ž๋ฅผ ํ†ตํ•ด ๋‘ ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ฐ™์€์ง€ ๋น„๊ตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Equatable์˜ ํŠน์ง•

  • == ์™€ != ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ตฌ์กฐ์ฒด๋‚˜ ์—ด๊ฑฐํ˜•์ฒ˜๋Ÿผ ๋‹จ์ˆœํ•œ ํƒ€์ž…์€ Swift๊ฐ€ ์ž๋™์œผ๋กœ Equatable์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

Equatable์€ ๋’ค์— ๋ฐฐ์šธ Comparable๊ณผ Hashable์˜ ๊ธฐ๋ฐ˜์ด ๋ฉ๋‹ˆ๋‹ค!

 

๊ทธ๋ ‡๋‹ค๋ฉด Equatable์€ ์–ธ์ œ ์‚ฌ์šฉํ• ๊นŒ์š”?

 

Equatable์€ ๋‘ ๊ฐ’์ด ์„œ๋กœ ๋™์ผํ•œ์ง€ ๋น„๊ตํ•ด์•ผ ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š”๋ฐ์š”.

์˜ˆ๋ฅผ ๋“ค์–ด, ํŠน์ • ์ผ๊ธฐ๊ฐ€ ๊ฐ™์€ ์ผ๊ธฐ์ธ์ง€, ํ˜น์€ ์‚ฌ์šฉ์ž ์ •๋ณด๊ฐ€ ๋™์ผํ•œ ๋ฐ์ดํ„ฐ์ธ์ง€ ํŒ๋‹จํ•  ๋•Œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ๋ฐฐ์—ด ๋‚ด์—์„œ ์ค‘๋ณต๋œ ์š”์†Œ๋ฅผ ์ œ๊ฑฐํ•˜๊ฑฐ๋‚˜, ํŠน์ • ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ์„ ๋•Œ๋„ ์ž์ฃผ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

 

์˜ˆ์‹œ

struct DiaryEntry: Equatable {
    let id: Int
    let title: String
}

let entry1 = DiaryEntry(id: 1, title: "์ฒซ ์ผ๊ธฐ")
let entry2 = DiaryEntry(id: 1, title: "์ฒซ ์ผ๊ธฐ")

print(entry1 == entry2)  // true

 

์ด์ฒ˜๋Ÿผ id์™€ title์ด ๋ชจ๋‘ ๊ฐ™์œผ๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

 

Swift๊ฐ€ ์ž๋™์œผ๋กœ == ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๋ฏ€๋กœ, ์ง์ ‘ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.

 

์ง์ ‘ ๋น„๊ต ๊ธฐ์ค€์„ ์ •ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

struct DiaryEntry: Equatable {
    let id: Int
    let title: String

    static func == (lhs: DiaryEntry, rhs: DiaryEntry) -> Bool {
        return lhs.id == rhs.id
    }
}

 

์ฃผ์˜ํ•  ์ 

  • a == a, a == b ⇒ b == a, a == b && b == c ⇒ a == c์ฒ˜๋Ÿผ ๋…ผ๋ฆฌ์ ์œผ๋กœ ์ผ๊ด€์„ฑ์ด ์œ ์ง€๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ธฐ๋ณธ ํƒ€์ž…(Int, String ๋“ฑ)์€ ์ด๋ฏธ Equatable์„ ์ฑ„ํƒํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋‹จ์ˆœ ๋น„๊ต ์ด์ƒ์˜ ๊ธฐ๋Šฅ(์ •๋ ฌ, ๊ฒ€์ƒ‰ ๋“ฑ)์ด ํ•„์š”ํ•˜๋‹ค๋ฉด Comparable ๋˜๋Š” Hashable์„ ํ•จ๊ป˜ ๊ณ ๋ คํ•ฉ๋‹ˆ๋‹ค.

 

๐ŸŽ Comparable

Comparable์€ ๊ฐ’ ์‚ฌ์ด์˜ ์ˆœ์„œ๋ฅผ ๋น„๊ตํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ํ”„๋กœํ† ์ฝœ์ž…๋‹ˆ๋‹ค.

“Comparable types can be compared using relational operators like <, <=, >, >=.”

 

์ฆ‰, <, >, <=, >= ๊ฐ™์€ ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Comparable์˜ ํŠน์ง•

  • Comparable์€ Equatable์„ ์ƒ์†ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • < ๋งŒ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋ฉด ๋‚˜๋จธ์ง€ ์—ฐ์‚ฐ์ž(<=, >, >=)๋Š” Swift๊ฐ€ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • sorted(), min(), max() ๋“ฑ๊ณผ ํ•จ๊ป˜ ์ž์ฃผ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

 

๊ทธ๋ ‡๋‹ค๋ฉด ์™œ <๋Š” ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ• ๊นŒ์š”?

๊ทธ ์ด์œ ๋Š” ํƒ€์ž…๋งˆ๋‹ค ‘์ž‘๋‹ค’์˜ ๊ธฐ์ค€์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค!

 

์˜ˆ๋ฅผ ๋“ค์–ด, ๋‚ ์งœ๋ฅผ ๋น„๊ตํ•  ๋•Œ๋Š” ๋” ์ด์ „์˜ ๋‚ ์งœ๊ฐ€ ์ž‘๋‹ค๊ณ  ํŒ๋‹จํ•ด์•ผ ํ•˜๊ณ , ์ ์ˆ˜๋ฅผ ๋น„๊ตํ•  ๋•Œ๋Š” ๋” ๋‚ฎ์€ ์ ์ˆ˜๊ฐ€ ์ž‘๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์ฒ˜๋Ÿผ “๋ฌด์—‡์ด ๋” ์ž‘์€๊ฐ€”์˜ ๊ธฐ์ค€์€ ํƒ€์ž…๋งˆ๋‹ค ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์—, Swift๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ < ์—ฐ์‚ฐ์˜ ์˜๋ฏธ๋ฅผ ์ •์˜ํ•˜๋„๋ก ์š”๊ตฌํ•˜๋Š” ๊ฑฐ์ฃ .

Comparable์€ ๋‚ ์งœ, ์ ์ˆ˜, ์ˆœ์œ„์ฒ˜๋Ÿผ ์ˆœ์„œ๋ฅผ ์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ’์„ ๋น„๊ตํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

์˜ˆ๋ฅผ ๋“ค์–ด “์˜ค๋ž˜๋œ ๊ธ€๋ถ€ํ„ฐ ์ •๋ ฌํ•˜๊ธฐ” ๋˜๋Š” “์ข‹์•„์š”๊ฐ€ ๋งŽ์€ ์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜๊ธฐ” ๋“ฑ์˜ ์ƒํ™ฉ์—์„œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๊ฒ ์ฃ ?

 

์˜ˆ์‹œ

struct DiaryEntry: Comparable {
    let id: Int
    let date: Date

    static func < (lhs: DiaryEntry, rhs: DiaryEntry) -> Bool {
        return lhs.date < rhs.date
    }
}

let entries = [
    DiaryEntry(id: 1, date: Date(timeIntervalSinceNow: 3600)),
    DiaryEntry(id: 2, date: Date())
]

let sorted = entries.sorted()
print(sorted.map { $0.id })  // [2, 1] — ์˜ค๋ž˜๋œ ์ผ๊ธฐ๋ถ€ํ„ฐ ์ •๋ ฌ๋ฉ๋‹ˆ๋‹ค.

 

ํ•ด๋‹น ์ฝ”๋“œ๋Š” ๋‚ ์งœ๋ฅผ ๊ธฐ์ค€์œผ๋กœ < ์—ฐ์‚ฐ์„ ์ •์˜ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—, ์ •๋ ฌ ์‹œ ์ž๋™์œผ๋กœ “์ด์ „ ๋‚ ์งœ → ์ดํ›„ ๋‚ ์งœ” ์ˆœ์„œ๋กœ ์ •๋ ฌ๋ฉ๋‹ˆ๋‹ค.

 

์ฃผ์˜ํ•  ์ 

  • a < b๋ผ๋ฉด ๋ฐ˜๋“œ์‹œ a != b์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์ •๋ ฌ ๊ธฐ์ค€์ด ๋ฐ”๋€Œ๋ฉด ์ฝ”๋“œ ์ „๋ฐ˜์˜ ๊ฒฐ๊ณผ๋„ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์ผ๊ด€๋œ ๊ธฐ์ค€์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.
  • Comparable์„ ์ฑ„ํƒํ•˜๋ฉด Equatable ๊ธฐ๋Šฅ๋„ ์ž๋™์œผ๋กœ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

 

๐ŸŽ Hashable

Hashable์€ ๊ฐ์ฒด๋ฅผ ๊ณ ์œ ํ•œ ํ•ด์‹œ๊ฐ’(hash value) ์œผ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ํ”„๋กœํ† ์ฝœ์ž…๋‹ˆ๋‹ค.

“A type that conforms to Hashable can be used as a key in a dictionary or as an element in a set.”

 

์ฆ‰, Hashable์„ ์ฑ„ํƒํ•œ ํƒ€์ž…์€ Dictionary์˜ ํ‚ค๋‚˜ Set์˜ ์š”์†Œ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

Hashable์˜ ํŠน์ง•

  • hash(into:) ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ณ ์œ ํ•œ ํ•ด์‹œ ๊ฐ’์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐ™์€ ๊ฐ์ฒด๋ผ๋ฉด ํ•ญ์ƒ ๊ฐ™์€ ํ•ด์‹œ ๊ฐ’์„ ๊ฐ€์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ตฌ์กฐ์ฒด๋‚˜ ์—ด๊ฑฐํ˜•์˜ ๊ฒฝ์šฐ ๋Œ€๋ถ€๋ถ„ ์ž๋™์œผ๋กœ Hashable์ด ๊ตฌํ˜„๋ฉ๋‹ˆ๋‹ค.

Hashable์€ Dictionary์˜ ํ‚ค๋กœ ์ปค์Šคํ…€ ํƒ€์ž…์„ ์‚ฌ์šฉํ•ด์•ผ ํ•  ๋•Œ, ์ค‘๋ณต๋˜์ง€ ์•Š๋Š” Set ์ปฌ๋ ‰์…˜์„ ๋งŒ๋“ค ๋•Œ,

๋˜๋Š” ๊ณ ์œ  ์‹๋ณ„์ž๋ฅผ ๊ฐ€์ง„ ๊ฐ์ฒด๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๋น„๊ตํ•˜๊ฑฐ๋‚˜ ๊ด€๋ฆฌํ•ด์•ผ ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

์˜ˆ์‹œ

struct Topic: Hashable {
    let id: UUID
    let name: String
}

var topicSet: Set<Topic> = []
let topicA = Topic(id: UUID(), name: "ํ•˜์ด๋ง๊ตฌ์–ผ ์ผ๊ธฐ")
let topicB = Topic(id: UUID(), name: "์ดˆ๋ก์‚ฌ๊ณผ๋ฐ˜ ์Šคํ„ฐ๋””")

topicSet.insert(topicA)
topicSet.insert(topicB)

print(topicSet.contains(topicA))  // true

์•„๋ž˜์ฒ˜๋Ÿผ ์ง์ ‘ ํ•ด์‹œ ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

struct Topic: Hashable {
    let id: UUID
    let name: String

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
        hasher.combine(name)
    }
}

 

์ฃผ์˜ํ•  ์ 

  • == ๋น„๊ต์— ์‚ฌ์šฉํ•œ ํ”„๋กœํผํ‹ฐ์™€ hash(into:)์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœํผํ‹ฐ๋Š” ๋™์ผํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ํ•ด์‹œ๊ฐ’์ด ๊ฐ™๋‹ค๊ณ  ํ•ด์„œ ํ•ญ์ƒ ๊ฐ™์€ ๊ฐ์ฒด์ธ ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค(์ถฉ๋Œ ๊ฐ€๋Šฅ์„ฑ ์กด์žฌ).
  • ๋‹จ์ˆœํ•œ ๊ตฌ์กฐ์ฒด๋ผ๋„ id๋‚˜ name์ฒ˜๋Ÿผ ๊ณ ์œ  ์‹๋ณ„์ž๊ฐ€ ์žˆ๋‹ค๋ฉด ์ˆ˜๋™์œผ๋กœ hash(into:)๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค.

 

๋งˆ์ง€๋ง‰์œผ๋กœ ๊ฐ ํ”„๋กœํ† ์ฝœ์„ ๋น„๊ตํ•ด๋ณด๋ฉด์„œ ์ •๋ฆฌํ•ด๋ด…์‹œ๋‹ค!

ํ”„๋กœํ† ์ฝœ  ๋ชฉ์  ํ•ต์‹ฌ ์—ฐ์‚ฐ์ž/๋ฉ”์„œ๋“œ ์ฃผ์š” ์‚ฌ์šฉ์ฒ˜
Equatable ๋‘ ๊ฐ’์ด ๊ฐ™์€์ง€ ๋น„๊ต ==, != ๊ฐ’ ๋น„๊ต, ์ค‘๋ณต ์ œ๊ฑฐ
Comparable ์ˆœ์„œ๋ฅผ ๋น„๊ต <, >, <=, >= ์ •๋ ฌ, ์ˆœ์œ„ ๋งค๊น€
Hashable ๊ฐ’์„ ํ•ด์‹œํ™” hash(into:), == Dictionary ํ‚ค, Set ์š”์†Œ

 

๋ฐ˜์‘ํ˜•