0Pricing
Swift Academy · Урок

Hashable и hash(into:)

Используйте типы как ключи словаря и элементы множества.

«Hashable и hash(into:)» — бесплатный урок Swift Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Swift Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Swift Academy содержит 4 уроков всего.

Что такое хешируемость

Hashable расширяет возможности Equatable и предоставляет хеш-значение. Это необходимо, чтобы использовать тип в качестве элемента Set или ключа Dictionary.

Автоматическое хеширование

Как и для Equatable, для структур и перечислений с хешируемыми элементами соответствие синтезируется автоматически:

struct Point: Hashable {
    let x: Int
    let y: Int
}
let set: Set<Point> = [Point(x: 0, y: 0), Point(x: 0, y: 0)]
print(set.count)  // 1  -- duplicate collapsed

Использование типа в качестве ключа словаря

Хешируемые типы можно использовать как ключи словаря:

struct Coord: Hashable { let r: Int; let c: Int }
var grid: [Coord: String] = [:]
grid[Coord(r: 0, c: 0)] = "start"
print(grid[Coord(r: 0, c: 0)]!)  // "start"

Контракт хешируемости

Два равных значения (==) MUST выдавать один и тот же хеш. Если Вы настраиваете один из этих механизмов, согласуйте их между собой, иначе множества и словари будут работать неправильно.

Пользовательский hash(into:)

Реализуйте hash(into:), передавая хешеру соответствующие свойства:

struct User: Hashable {
    let id: Int
    let name: String
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
    static func == (l: User, r: User) -> Bool { l.id == r.id }
}
print(Set([User(id: 1, name: "A"), User(id: 1, name: "B")]).count)  // 1

Объединение нескольких полей

Передавайте каждое поле, участвующее в проверке равенства:

struct Pair: Hashable {
    let a: Int
    let b: String
    func hash(into hasher: inout Hasher) {
        hasher.combine(a)
        hasher.combine(b)
    }
}
print(Pair(a: 1, b: "x").hashValue == Pair(a: 1, b: "x").hashValue)  // true

Почему следует хешировать только поля идентичности

Хешируйте только поля, используемые в ==. Добавление дополнительных изменяемых полей может нарушить правило: из равенства значений должно следовать равенство их хешей:

struct Doc: Hashable {
    let id: Int       // identity
    var title: String // not part of equality
    func hash(into hasher: inout Hasher) { hasher.combine(id) }
    static func == (l: Doc, r: Doc) -> Bool { l.id == r.id }
}
print(Doc(id: 1, title: "v1") == Doc(id: 1, title: "v2"))  // true

Хешируемые перечисления

Перечисления автоматически становятся хешируемыми, в том числе с ассоциированными значениями, если сами эти значения поддерживают хеширование:

enum Direction: Hashable { case north, south, east, west }
let visited: Set<Direction> = [.north, .north, .east]
print(visited.count)  // 2

Удаление дубликатов с помощью множества

Распространённый случай использования: удаление дубликатов за счёт преобразования значений в Set:

let nums = [1, 2, 2, 3, 3, 3]
let unique = Array(Set(nums)).sorted()
print(unique)  // [1, 2, 3]

Хешер рандомизируется при каждом запуске

Хеш-значения получают начальное значение при каждом запуске программы, поэтому никогда не сохраняйте hashValue на диске и не рассчитывайте на его точное число между запусками:

struct K: Hashable { let v: Int }
let h1 = K(v: 5).hashValue
// h1 is stable within this run only
print(K(v: 5) == K(v: 5))  // true  -- equality is what to rely on

Подсчёт с помощью словаря

Хешируемые ключи позволяют подсчитывать частоты — это очень распространённый шаблон:

let letters = ["a", "b", "a", "c", "b", "a"]
var counts: [String: Int] = [:]
for l in letters { counts[l, default: 0] += 1 }
print(counts["a"]!)  // 3

Быстрая проверка

Какое правило должен соблюдать hash(into:) относительно ==?

Итоги

Вы изучили Hashable:

  • Hashable расширяет Equatable и требуется для ключей Set/Dictionary
  • Синтезируется для структур и перечислений с хешируемыми элементами
  • В пользовательском hash(into:) объединяйте только поля идентичности, используемые в ==
  • Равные значения должны иметь одинаковые хеши; хеши рандомизируются при каждом запуске

Далее: сопоставимость и сортировка.

Часто задаваемые вопросы

Урок «Hashable и hash(into:)» бесплатный?

Да — полный текст урока «Hashable и hash(into:)» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Swift Academy, подпишись на CoddyKit PRO. Курс Swift Academy содержит 4 уроков всего.

Чему я научусь в уроке «Hashable и hash(into:)»?

Используйте типы как ключи словаря и элементы множества. Ты практикуешь Swift Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Swift Academy?

Предыдущий опыт не требуется. Swift Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Hashable и hash(into:)»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Swift Academy?

Да. Каждый урок Swift Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Синтезированный Equatable
  2. Hashable и hash(into:)
  3. Comparable и сортировка
  4. Пользовательская семантика равенства
← Назад к Swift Academy