Как согласовать настраиваемый класс с дополнительными свойствами с хешируемым протоколом

Предположим, у меня есть базовый класс «Человек», который я хочу добавить в Set (List) и, следовательно, должен соответствовать Hashable и Equatable:

class Person : Equatable, Hashable {
let firstName: String
let lastName: String
var nickname: String?
let dateOfBirth: NSDate
var hashValue: Int {
    if let nickname = nickname {
        return firstName.hashValue ^
               lastName.hashValue ^
               nickname.hashValue ^
               dateOfBirth.hashValue
    } else {
        return firstName.hashValue ^
               lastName.hashValue ^
               dateOfBirth.hashValue
    }

}

init (firstName: String, lastName: String, nickname: String, bornOn dateOfBirth: NSDate) {
    self.firstName = firstName
    self.lastName  = lastName
    self.nickname = nickname
    self.dateOfBirth = dateOfBirth
    }
}

func ==(lhs: Person, rhs: Person) -> Bool {
    return
        lhs.firstName   == rhs.firstName    &&
        lhs.lastName    == rhs.lastName     &&
        lhs.nickname    == rhs.nickname     &&
        lhs.dateOfBirth == rhs.dateOfBirth
}

Этот класс имеет только одно необязательное свойство, что делает работу с необязательным при создании хэш-значения достаточно разумной. Что, если бы было 2 или более необязательных свойства? Я вижу, что это довольно быстро выходит из-под контроля.

Как обычно обрабатываются необязательные свойства при согласовании объекта с хешируемым протоколом?


person BrandonLenz    schedule 12.03.2017    source источник


Ответы (2)


Расчет хеш-значения не обязательно должен основываться на всех свойствах. Фактически, это не обязательно должно основываться ни на чем. Вы можете просто вернуть жестко закодированный номер, но не должны.

Просто верните хеш-значение одного или нескольких необязательных свойств.

Единственное правило состоит в том, что два объекта, которые сравниваются как равные, также должны возвращать одно и то же хеш-значение. Но нет требования, чтобы два объекта с одинаковым хеш-значением сравнивались как равные.

person rmaddy    schedule 13.03.2017
comment
Звучит разумно. Возможно, это тема для отдельного обсуждения, но: Есть ли какой-либо недостаток в снижении специфичности вычисления hashValue, который может вызвать проблемы в будущем? - person BrandonLenz; 13.03.2017
comment
Плохое значение хеш-функции может привести к проблемам с производительностью при хранении объектов в коллекции. - person rmaddy; 13.03.2017
comment
hashValue также можно использовать для оптимизации ==, когда == стоит дорого. Вы можете быстро отбросить явно неравные значения, если их хеши не равны. hashValue должен быть O (1) для вычисления. - person Rob Napier; 13.03.2017
comment
@RobNapier умный! Имеет смысл. Спасибо за дополнительный совет. - person BrandonLenz; 13.03.2017

Я просто хотел бы добавить, что класс не должен соответствовать протоколу Hashable или Equatable для использования в качестве элементов массива (это то, что я предположил, вы имели в виду под списком). Соответствовать Hashable и, следовательно, Equatable необходимо только в том случае, если вы хотите использовать класс как элемент в наборе или как ключ для словаря. Как указано в справочнике по API Hashable:

Вы можете использовать любой тип, соответствующий протоколу Hashable, в наборе или в качестве словарного ключа.

person J.beenie    schedule 13.03.2017
comment
Вам необходимо соответствовать Equatable, если вы хотите искать объекты в массиве, используя, например, методы index(of:) или contains. - person rmaddy; 13.03.2017
comment
Да, для удобства, если вы не хотите передавать закрытие для index(of:) и contains и передавать только сам элемент. - person J.beenie; 14.03.2017