Интерфейс Set

Set<E> — коллекция, которая запрещает дубликаты: метод add(e) возвращает false, если такой элемент уже есть. По математической модели Set — это множество.

Реализации различаются порядком хранения и стоимостью операций:

  • HashSet — основан на хэш-таблице. Порядок элементов не определён. add, remove, contains за O(1) в среднем.

  • LinkedHashSet — сохраняет порядок вставки. Чуть медленнее HashSet.

  • TreeSet — отсортированное множество (SortedSet/NavigableSet). Операции за O(log n).

  • EnumSet — компактное множество для значений enum.

Иерархия

Collection<E>
    └── Set<E>
            ├── HashSet<E>
            │       └── LinkedHashSet<E>
            ├── SortedSet<E>
            │       └── NavigableSet<E>
            │               └── TreeSet<E>
            └── EnumSet<E>

Пример 1. HashSet — устранение дубликатов

import java.util.HashSet;
import java.util.Set;

public class SetHashDemo {
    public static void main(String[] args) {
        Set<String> sensors = new HashSet<>();
        sensors.add("HC-SR04");
        sensors.add("DHT22");
        sensors.add("HC-SR04");  // дубликат
        sensors.add("MPU6050");

        System.out.println("Размер: " + sensors.size());
        System.out.println("Множество: " + sensors);
    }
}

Output:

Размер: 3
Множество: [MPU6050, DHT22, HC-SR04]

Пример 2. LinkedHashSet — порядок вставки

import java.util.LinkedHashSet;
import java.util.Set;

public class SetLinkedDemo {
    public static void main(String[] args) {
        Set<String> path = new LinkedHashSet<>();
        path.add("start");
        path.add("checkpoint");
        path.add("turn");
        path.add("finish");
        path.add("start"); // не добавится

        for (String step : path) {
            System.out.println(step);
        }
    }
}

Output:

start
checkpoint
turn
finish

Пример 3. TreeSet — отсортированное множество

import java.util.TreeSet;

public class SetTreeDemo {
    public static void main(String[] args) {
        TreeSet<Integer> distances = new TreeSet<>();
        distances.add(45);
        distances.add(12);
        distances.add(87);
        distances.add(30);

        System.out.println("Отсортировано: " + distances);
        System.out.println("Минимум: " + distances.first());
        System.out.println("Максимум: " + distances.last());
        System.out.println("Ближайшее снизу к 50: " + distances.floor(50));
        System.out.println("Ближайшее сверху к 50: " + distances.ceiling(50));
    }
}

Output:

Отсортировано: [12, 30, 45, 87]
Минимум: 12
Максимум: 87
Ближайшее снизу к 50: 45
Ближайшее сверху к 50: 87

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

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class SetOperations {
    public static void main(String[] args) {
        Set<String> a = new HashSet<>(Arrays.asList("D2", "D4", "D5"));
        Set<String> b = new HashSet<>(Arrays.asList("D5", "D6", "D8"));

        Set<String> union = new HashSet<>(a);
        union.addAll(b);
        Set<String> inter = new HashSet<>(a);
        inter.retainAll(b);
        Set<String> diff = new HashSet<>(a);
        diff.removeAll(b);

        System.out.println("Объединение: " + union);
        System.out.println("Пересечение: " + inter);
        System.out.println("Разность A\\B: " + diff);
    }
}

Output:

Объединение: [D2, D4, D5, D6, D8]
Пересечение: [D5]
Разность A\B: [D2, D4]

Пример 5. EnumSet для режимов робота

import java.util.EnumSet;
import java.util.Set;

public class SetEnumDemo {
    enum Mode { MANUAL, AUTO, LINE, OBSTACLE, IR }

    public static void main(String[] args) {
        Set<Mode> active = EnumSet.of(Mode.AUTO, Mode.LINE);
        System.out.println("Активные: " + active);
        System.out.println("Авто? " + active.contains(Mode.AUTO));

        Set<Mode> all = EnumSet.allOf(Mode.class);
        System.out.println("Все режимы: " + all);
    }
}

Output:

Активные: [AUTO, LINE]
Авто? true
Все режимы: [MANUAL, AUTO, LINE, OBSTACLE, IR]

Подводные камни

  • Для корректной работы HashSet тип элементов обязан правильно реализовать hashCode() и equals(). Иначе одинаковые объекты будут считаться разными.

  • TreeSet требует, чтобы элементы реализовывали Comparable или был передан Comparator. Иначе — ClassCastException.

  • HashSet не гарантирует порядка обхода — порядок может меняться от версии JVM.

  • null в TreeSet запрещён, в HashSet — допускается один.

  • Не изменяйте поля объекта после добавления в HashSet, если эти поля участвуют в hashCode().

См. также

Примечание

Материал подготовлен на основе официального руководства Oracle The Collections Framework и распространяется на условиях Oracle Free Documentation License. Тексты переписаны своими словами, примеры кода — оригинальные.