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

Какие знаешь особенности Set?

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

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

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

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

Особенности Set интерфейса в Java

Set — это один из основных интерфейсов коллекций, который представляет неупорядоченное множество уникальных элементов. Это мощный инструмент для работы с данными, когда нужны гарантии отсутствия дубликатов.

Основные характеристики Set

1. Уникальность элементов

Set не содержит дубликатов. Два элемента считаются одинаковыми, если метод equals() возвращает true:

Set<String> fruits = new HashSet<>();
fruits.add("apple");
fruits.add("banana");
fruits.add("apple");  // Не добавится, так как уже есть

System.out.println(fruits.size());  // 2
System.out.println(fruits);  // [apple, banana] или [banana, apple]

2. Отсутствие порядка (в большинстве реализаций)

Обычный HashSet не гарантирует порядок элементов:

Set<Integer> numbers = new HashSet<>();
for (int i = 1; i <= 5; i++) {
    numbers.add(i);
}
System.out.println(numbers);  // Порядок непредсказуем: [1, 3, 5, 2, 4]

3. Вместимость Null

В HashSet может быть один null элемент:

Set<String> items = new HashSet<>();
items.add(null);
items.add("item1");
items.add(null);  // Не добавится
System.out.println(items.size());  // 2

В TreeSet null не допускается:

Set<Integer> sorted = new TreeSet<>();
sorted.add(null);  // Выбросит NullPointerException

Основные реализации Set

1. HashSet — быстро, но без порядка

Set<String> hashSet = new HashSet<>();
hashSet.add("zebra");
hashSet.add("apple");
hashSet.add("banana");

for (String fruit : hashSet) {
    System.out.println(fruit);  // Порядок случайный
}

// Характеристики:
// - O(1) add, remove, contains (в среднем)
// - О(n) в худшем случае (при коллизиях хеша)
// - Не потокобезопасен

2. TreeSet — упорядочен, но медленнее

Set<String> treeSet = new TreeSet<>();
treeSet.add("zebra");
treeSet.add("apple");
treeSet.add("banana");

for (String fruit : treeSet) {
    System.out.println(fruit);  // apple, banana, zebra (отсортировано)
}

// Характеристики:
// - O(log n) add, remove, contains
// - Элементы отсортированы в естественном порядке
// - Реализует SortedSet интерфейс
// - Не потокобезопасен

3. LinkedHashSet — порядок вставки

Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("zebra");
linkedHashSet.add("apple");
linkedHashSet.add("banana");

for (String fruit : linkedHashSet) {
    System.out.println(fruit);  // zebra, apple, banana (порядок вставки)
}

// Характеристики:
// - O(1) add, remove, contains (как HashSet)
// - Сохраняет порядок вставки
// - Чуть больше памяти чем HashSet

4. CopyOnWriteArraySet — thread-safe

Set<String> threadSafeSet = new CopyOnWriteArraySet<>();
threadSafeSet.add("item1");
threadSafeSet.add("item2");

// Безопасен для многопоточности
// Копирует массив при каждой модификации
// Хорош когда модификаций мало

Основные методы Set

Set<Integer> numbers = new HashSet<>(Arrays.asList(1, 2, 3));

// add() — добавить элемент
boolean added = numbers.add(4);  // true
boolean notAdded = numbers.add(2);  // false (уже есть)

// remove() — удалить элемент
boolean removed = numbers.remove(1);  // true
boolean notRemoved = numbers.remove(10);  // false (нет такого)

// contains() — проверить наличие
boolean exists = numbers.contains(2);  // true

// size() и isEmpty()
int size = numbers.size();
boolean empty = numbers.isEmpty();

// clear() — удалить всё
numbers.clear();

// addAll(), removeAll(), retainAll() — массовые операции
Set<Integer> set1 = new HashSet<>(Arrays.asList(1, 2, 3));
Set<Integer> set2 = new HashSet<>(Arrays.asList(2, 3, 4));

set1.addAll(set2);      // {1, 2, 3, 4}
set1.removeAll(set2);   // удалить пересечение
set1.retainAll(set2);   // оставить только пересечение

Операции над множествами

Set<Integer> a = new HashSet<>(Arrays.asList(1, 2, 3, 4));
Set<Integer> b = new HashSet<>(Arrays.asList(3, 4, 5, 6));

// Объединение (Union)
Set<Integer> union = new HashSet<>(a);
union.addAll(b);
System.out.println(union);  // [1, 2, 3, 4, 5, 6]

// Пересечение (Intersection)
Set<Integer> intersection = new HashSet<>(a);
intersection.retainAll(b);
System.out.println(intersection);  // [3, 4]

// Разность (Difference)
Set<Integer> difference = new HashSet<>(a);
difference.removeAll(b);
System.out.println(difference);  // [1, 2]

// Проверка подмножества
boolean isSubset = a.containsAll(intersection);  // true
boolean isSuperset = intersection.containsAll(a);  // false

Пользовательские объекты в Set

Для правильной работы с объектами нужно реализовать equals() и hashCode():

class Person {
    private String name;
    private int age;
    
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) 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> people = new HashSet<>();
people.add(new Person("John", 30));
people.add(new Person("Jane", 25));
people.add(new Person("John", 30));  // Не добавится

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

SortedSet и NavigableSet

// SortedSet — отсортированное множество
SortedSet<Integer> sortedSet = new TreeSet<>(Arrays.asList(5, 2, 8, 1, 9));
System.out.println(sortedSet);  // [1, 2, 5, 8, 9]

// Методы SortedSet
Integer first = sortedSet.first();    // 1
Integer last = sortedSet.last();      // 9

// NavigableSet — дополнительные методы навигации
NavigableSet<Integer> navSet = new TreeSet<>(Arrays.asList(1, 3, 5, 7, 9));
Integer ceiling = navSet.ceiling(4);  // 5 (первое >= 4)
Integer floor = navSet.floor(4);      // 3 (первое <= 4)
Integer higher = navSet.higher(5);    // 7 (первое > 5)
Integer lower = navSet.lower(5);      // 3 (первое < 5)

// Диапазоны
SortedSet<Integer> subset = navSet.subSet(3, 8);  // [3, 5, 7]
SortedSet<Integer> headSet = navSet.headSet(5);   // [1, 3]
SortedSet<Integer> tailSet = navSet.tailSet(5);   // [5, 7, 9]

Сравнение реализаций

ХарактеристикаHashSetTreeSetLinkedHashSet
ПорядокНетОтсортированоПорядок вставки
add/remove/containsO(1)O(log n)O(1)
NullДопустимНетДопустим
ПотокобезопасенНетНетНет
Использование памятиМинимальноСреднееБольше (двусвязный список)
ИтерацияСлучайный порядокОтсортированныйПорядок вставки

Практический пример

public class UniqueUserCollector {
    private Set<String> visitedUsers = new HashSet<>();
    private Set<String> activeUsers = new TreeSet<>();
    
    public void recordVisit(String userId) {
        visitedUsers.add(userId);
    }
    
    public void setActive(String userId) {
        activeUsers.add(userId);
    }
    
    public Set<String> getUniqueVisitors() {
        return Collections.unmodifiableSet(visitedUsers);
    }
    
    public List<String> getActiveUsersSorted() {
        return new ArrayList<>(activeUsers);
    }
    
    public Set<String> getInactiveUsers() {
        Set<String> inactive = new HashSet<>(visitedUsers);
        inactive.removeAll(activeUsers);
        return inactive;
    }
}

Set — это незаменимый инструмент для работы с уникальными элементами. Выбор реализации зависит от требований к производительности и порядку элементов.

Какие знаешь особенности Set? | PrepBro