Как обеспечить уникальность хэша, если поля объекта не уникальны
Что проверяют
Понимаете ли вы, что хэш по определению не уникален, и какую задачу вы на самом деле решаете: различать объекты в Set и Dictionary или получить устойчивый отпечаток данных. Следом обычно спрашивают про контракт Hashable и Equatable и о том, что будет при его нарушении.
Как отвечать
Начните с поправки к вопросу: уникальный хэш невозможен, значений хэша конечное число, а объектов нет. Уникальность обеспечивает равенство, хэш только быстро сужает поиск. Дальше по задаче:
- контракт: если
a == b, хэши обязаны совпасть, обратное не требуется. Реализуйтеhash(into:)и передавайте вhasher.combine(_:)ровно те поля, что участвуют в==. СвойствоhashValueсамому реализовывать не нужно, как точка настройки оно устарело; - объекты с одинаковыми полями должны различаться: значит, у них есть идентичность, которой нет в полях. Добавьте
let id = UUID(), сравнивайте и хэшируйте по нему, заодно получитеIdentifiable. Для классов естьObjectIdentifier(self)и===, но это идентичность экземпляра в памяти, а не сущности; - устойчивый отпечаток для кеша, дедупликации или сервера:
Hasherзасевается случайно при каждом запуске, поэтомуhashValueнельзя сохранять на диск и отправлять по сети. Нужен криптографический хэш из CryptoKit,SHA256.hash(data:), от канонического представления объекта; - каноническое представление: фиксированный порядок полей и разделитель, который не встречается в данных, или длина перед каждым полем. Иначе пары
("ab", "c")и("a", "bc")склеятся в одну строку.
Где спотыкаются
Добавляют в хэш время создания или случайное число, но не добавляют их в ==. Контракт нарушен: равные объекты попадают в разные корзины, и Set начинает хранить дубли.
Сохраняют hashValue в UserDefaults или базу, а после перезапуска приложения не находят ни одной записи: значения уже другие.
Предлагают MD5, чтобы стало меньше коллизий. Для словаря это лишняя работа, а как защитный хэш MD5 давно сломан. Если нужен криптографический, берите SHA-256.