Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Множества: Set

Множество (Set) хранит только уникальные элементы: повторное добавление ничего не меняет. Оно отвечает на вопрос «есть ли такой элемент?» намного быстрее списка.

HashSet

Основная реализация множества — HashSet. Метод add() возвращает true, если элемент добавлен, и false, если такой уже был:

void main() {
    Set<String> tags = new HashSet<>();
    IO.println(tags.add("java"));
    IO.println(tags.add("java"));
    IO.println(tags.size());
}

Результат:

true
false
1

Чтобы понять, что элемент уже есть, множество вызывает hashCode() и equals(). Строки, числа и записи умеют это из коробки, а в своих классах эти методы нужно переопределить — иначе два одинаковых объекта окажутся в множестве дважды.

Проверка наличия

Метод contains() у множества работает почти мгновенно даже на миллионе элементов, тогда как список проверяет элементы по одному. Поэтому множество выбирают, когда нужно часто спрашивать «встречалось ли уже?»:

void main() {
    Set<String> banned = Set.of("спам", "реклама");
    String word = "спам";
    if (banned.contains(word)) {
        IO.println("Слово запрещено: " + word);
    }
}

Результат:

Слово запрещено: спам

Как и List.of(), метод Set.of() создаёт неизменяемое множество.

Порядок элементов

HashSet не хранит порядок добавления: элементы лежат так, как удобно для быстрого поиска. Если порядок важен, есть две другие реализации. LinkedHashSet помнит порядок добавления, а TreeSet хранит элементы отсортированными:

void main() {
    Set<String> linked = new LinkedHashSet<>();
    Set<String> sorted = new TreeSet<>();
    for (String city : List.of("Рим", "Берлин", "Осло", "Берлин")) {
        linked.add(city);
        sorted.add(city);
    }
    IO.println(linked);
    IO.println(sorted);
}

Результат:

[Рим, Берлин, Осло]
[Берлин, Осло, Рим]

Убрать повторы из списка

Частый приём: передать список в конструктор множества — дубликаты исчезнут сами. LinkedHashSet при этом сохранит исходный порядок:

void main() {
    List<Integer> visits = List.of(3, 1, 3, 2, 1);
    List<Integer> unique = new ArrayList<>(new LinkedHashSet<>(visits));
    IO.println(unique);
}

Результат:

[3, 1, 2]

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

Методы addAll(), retainAll() и removeAll() дают объединение, пересечение и разность. Они меняют множество, у которого вызваны, поэтому сначала делают копию:

void main() {
    Set<String> anna = Set.of("кино", "книги", "бег");
    Set<String> oleg = Set.of("бег", "шахматы", "кино");
 
    Set<String> common = new TreeSet<>(anna);
    common.retainAll(oleg);
    IO.println("Общие: " + common);
 
    Set<String> onlyAnna = new TreeSet<>(anna);
    onlyAnna.removeAll(oleg);
    IO.println("Только у Анны: " + onlyAnna);
}

Результат:

Общие: [бег, кино]
Только у Анны: [книги]

Частая ошибка

Set.of() не принимает повторяющиеся значения: вместо того чтобы молча убрать дубликат, он выбрасывает исключение:

void main() {
    Set<String> colors = Set.of("red", "green", "red");
}

Результат:

Exception in thread "main" java.lang.IllegalArgumentException: duplicate element: red

Если данные могут повторяться, создавайте множество через new HashSet<>(List.of(...)).

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.