Ответ
В Dart уникальность элементов в Set обеспечивается через корректную реализацию методов hashCode и оператора == (operator ==). Set использует хэш-таблицу, и для сравнения объектов сначала проверяет их хэш-коды, а затем, если хэш-коды совпали, вызывает == для точного сравнения.
Пример для кастомного класса:
class User {
final String id;
final String email;
User(this.id, this.email);
// Критически важно переопределить оба метода
@override
bool operator ==(Object other) {
return identical(this, other) ||
other is User &&
runtimeType == other.runtimeType &&
id == other.id; // Уникальность по ID
}
@override
int get hashCode => id.hashCode; // Хэш-код должен использовать те же поля, что и ==
}
void main() {
final users = <User>{};
final user1 = User('u1', 'alice@example.com');
final user2 = User('u1', 'alice.new@example.com'); // Тот же id
users.add(user1);
users.add(user2); // Не добавится, так как user2 == user1
print(users.length); // Вывод: 1
}
Для встроенных типов (String, int, etc.) эти методы уже корректно реализованы.
Ответ 18+ 🔞
Э, бля, смотри, сейчас объясню про эти ваши Set в Dart, чтобы ты не наступал на грабли, как последний распиздяй. Тут всё просто, как три копейки, но если накосячить — будет тебе хиросима и нигерсраки в коде.
Вот представь: Set — это такой хитрая жопа, которая хранит только уникальные штуки. А как она понимает, что вот этот объект уже есть, а этот — новенький? Да всё через два столпа, ёпта: hashCode и оператор ==. Это как паспорт и лицо. Сначала смотрит на паспорт (хэш-код), если паспорта разные — ну явно разные люди, добавляем. Если паспорта совпали — тогда уже пристально в рожу смотрит через ==. Если и рожи одинаковые — всё, это один и тот же чувак, второй раз не добавится.
И вот тут главный подвох, блядь: эти два метода должны быть заточены друг под друга, как вилка в розетку. Если в == ты сравниваешь по полю id, то и hashCode должен считать хэш только от этого id, а не от всей хуйни вместе взятой. Иначе получится пиздопроебибна: объекты будут казаться разными, а должны быть одинаковыми, и в Set начнётся овердохуища дублей.
Смотри, вот живой пример, чтобы не быть мартышлюшкой:
class User {
final String id;
final String email;
User(this.id, this.email);
// Вот тут решаем, кто есть кто. Сравниваем только по ID.
@override
bool operator ==(Object other) {
return identical(this, other) ||
other is User &&
runtimeType == other.runtimeType &&
id == other.id; // Всё! Email нас не ебёт для уникальности.
}
// А хэш-код — строго из того же поля! Иначе доверия ебать ноль.
@override
int get hashCode => id.hashCode;
}
void main() {
final users = <User>{};
final user1 = User('u1', 'alice@example.com');
final user2 = User('u1', 'alice.new@example.com'); // ID тот же самый, бля!
users.add(user1);
users.add(user2); // А вот эта запись не добавится, потому что user2 == user1
print(users.length); // Вывод: 1. Всё чётко, без сюрпризов.
}
Видишь? У второго юзера email другой, но id-то одинаковый! Значит, для нашего Set это один и тот же человек. Set посмотрел: хэш-коды от id совпали, потом дернул == — и да, id равны. Всё, второй объект — это хуй в пальто, а не новый элемент.
А со встроенными типами типа String или int можно не бздеть — там всё уже реализовано за нас, ебать копать. Но как только делаешь свой класс — сразу думай, по каким полям определяется его уникальность, и переопределяй оба метода. Иначе потом будешь охуевать, почему в твоём Set плавает три одинаковых объекта. Сам от себя охуеешь.