← Назад к вопросам

Может ли в Set храниться одинаковые элементы?

1.6 Junior🔥 211 комментариев
#Коллекции

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Может ли Set в Java содержать одинаковые элементы?

Нет, Set по определению не может содержать дублирующиеся (одинаковые) элементы. Это фундаментальная контрактная особенность интерфейса Set в Java Collections Framework.

Определение Set

public interface Set<E> extends Collection<E> {
    // Set не содержит дублирующихся элементов
    // Максимум один null (в зависимости от реализации)
}

В javadoc Set сказано:

"A collection that contains no duplicate elements."
(Коллекция, которая не содержит дублирующихся элементов)

Как это работает на практике

Пример 1: HashSet игнорирует дубликаты

Set<String> set = new HashSet<>();
set.add("hello");
set.add("world");
set.add("hello");  // Второй "hello" НЕ добавится

System.out.println(set.size());  // 2
System.out.println(set);  // [world, hello] или [hello, world]

Пример 2: add() возвращает false при дубликате

Set<Integer> set = new HashSet<>();
boolean added1 = set.add(1);  // true
boolean added2 = set.add(1);  // false (уже есть)
boolean added3 = set.add(2);  // true

System.out.println(set);  // [1, 2]

Метод add() возвращает boolean:

  • true — элемент был добавлен
  • false — элемент уже был в Set

Как Set определяет, что элементы одинаковые?

Через equals() и hashCode()

Для Set равенство элементов определяется методами equals() и hashCode():

public class Person {
    private String name;
    private int age;
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

Set<Person> set = new HashSet<>();
Person p1 = new Person("John", 30);
Person p2 = new Person("John", 30);  // Тот же человек
Person p3 = new Person("Jane", 25);

set.add(p1);  // true
set.add(p2);  // false - они равны (mismo name и age)
set.add(p3);  // true

System.out.println(set.size());  // 2

Разные реализации Set

1. HashSet — самая частая

Set<String> set = new HashSet<>();
set.add("a");
set.add("b");
set.add("a");  // Дубликат

System.out.println(set.size());  // 2

Использует HashMap внутри:

HashMap<E, Object> map = new HashMap<>();
// При add(element) выполняется map.put(element, PRESENT)

2. TreeSet — упорядоченный

Set<Integer> set = new TreeSet<>();
set.add(3);
set.add(1);
set.add(2);
set.add(1);  // Дубликат

System.out.println(set);  // [1, 2, 3]
System.out.println(set.size());  // 3

Использует compareTo() (не hashCode/equals):

Set<String> set = new TreeSet<>(new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return o1.compareTo(o2);
    }
});

3. LinkedHashSet — сохраняет порядок

Set<String> set = new LinkedHashSet<>();
set.add("first");
set.add("second");
set.add("first");  // Дубликат

System.out.println(set);  // [first, second]
System.out.println(set.size());  // 2

Проблема 1: Неправильно реализованные equals/hashCode

Опасный код:

public class BadPerson {
    private String name;
    
    // hashCode реализован, но equals НЕТ
    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
    // Используется equals из Object (сравнивает ссылки)
}

Set<BadPerson> set = new HashSet<>();
BadPerson p1 = new BadPerson("John");
BadPerson p2 = new BadPerson("John");

set.add(p1);  // true
set.add(p2);  // true (разные объекты!)

System.out.println(set.size());  // 2 (но это одинаковые люди!)

Правильный код:

public class GoodPerson {
    private String name;
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        GoodPerson person = (GoodPerson) o;
        return Objects.equals(name, person.name);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}

Set<GoodPerson> set = new HashSet<>();
GoodPerson p1 = new GoodPerson("John");
GoodPerson p2 = new GoodPerson("John");

set.add(p1);  // true
set.add(p2);  // false (они равны)

System.out.println(set.size());  // 1

Проблема 2: Изменение объекта после добавления

Очень опасный код:

public class MutablePerson {
    private String name;
    
    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MutablePerson person = (MutablePerson) o;
        return Objects.equals(name, person.name);
    }
}

Set<MutablePerson> set = new HashSet<>();
MutablePerson p = new MutablePerson();
p.setName("John");

set.add(p);  // Добавлена в bucket с hashCode для "John"

p.setName("Jane");  // ИЗМЕНИЛИ hashCode!

boolean found = set.contains(p);  // false! (ищет не в том bucket)
System.out.println(set.size());  // 1 (объект потерян)
System.out.println(set);  // [Jane] - невозможно найти!

Правило: Никогда не изменяй hashCode объекта после добавления в Set!

Решение: Используй immutable объекты:

public final class ImmutablePerson {
    private final String name;
    
    public ImmutablePerson(String name) {
        this.name = name;
    }
    // Нет setters!
    
    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ImmutablePerson person = (ImmutablePerson) o;
        return Objects.equals(name, person.name);
    }
}

TreeSet и compareTo()

TreeSet использует не equals(), а compareTo():

TreeSet<String> set = new TreeSet<>();
set.add("apple");
set.add("banana");
set.add("apple");  // Дубликат

System.out.println(set);  // [apple, banana]
System.out.println(set.size());  // 2

Для TreeSet важно:

public class CustomComparable implements Comparable<CustomComparable> {
    private int value;
    
    @Override
    public int compareTo(CustomComparable o) {
        // Если возвращает 0 -> считаются дубликатом!
        return Integer.compare(this.value, o.value);
    }
}

TreeSet<CustomComparable> set = new TreeSet<>();
set.add(new CustomComparable(1));
set.add(new CustomComparable(1));  // Дубликат (compareTo вернул 0)
set.add(new CustomComparable(2));

System.out.println(set.size());  // 2

Практическое правило

Для HashSet/LinkedHashSet:

// ОБЯЗАТЕЛЬНО реализуй:
@Override equals(Object o)  // Сравнение значений
@Override hashCode()        // Должно совпадать с equals

Для TreeSet:

// Используй compareTo() ИЛИ Comparator
public class Thing implements Comparable<Thing> {
    @Override
    public int compareTo(Thing o) {
        // Если = 0 -> дубликат
        return ..;
    }
}

Вывод

Нет, Set не может содержать одинаковые элементы. Это гарантируется:

  1. Контрактом интерфейса Set — так он определен
  2. Методом add() — возвращает false при дубликате
  3. equals()/hashCode() — правильное сравнение
  4. Автоматическим игнорированием дубликатов — при попытке добавить

Если тебе нужны дубликаты — используй List, не Set!

Может ли в Set храниться одинаковые элементы? | PrepBro