Set.clear(): примеры (JAVA)

Очистка множества в Java и смежные сценарии
Раздел: Коллекции, Set
Set.clear(): void

Описание метода

В Java метод clear присутствует в интерфейсе java.util.Collection и, следовательно, реализуется большинством реализаций Set (например, HashSet, TreeSet, LinkedHashSet). Сигнатура метода:

void clear()

Краткое поведение:

  • Удаляет все элементы из множества. После вызова size() возвращает 0, isEmpty() возвращает true.
  • Аргументы отсутствуют. Возвращаемое значение отсутствует (тип void).
  • Реализация зависит от конкретного класса: операция может быть оптимизирована для определённых структур данных, но семантика - удалить все элементы - сохраняется.
  • Если сет является представлением (view) другой коллекции, например map.keySet(), очистка представления влияет на исходную коллекцию (удаляются соответствующие элементы в исходном контейнере).

Возможные исключения и особенности:

  • UnsupportedOperationException - если конкретная реализация не поддерживает модификацию (например, неизменяемые множества, создаваемые через Set.of(...) или Collections.unmodifiableSet(...)).
  • ConcurrentModificationException - может возникнуть при обнаружении модификации коллекции во время итерации с помощью несинхронизированного итератора у реализаций, отслеживающих изменения модификаций. Сам по себе clear() не гарантирует атомарность для всех реализаций и может быть видим по-разному в многопоточной среде.
  • Для конкурентных реализаций (например, ConcurrentSkipListSet или наборов на основе ConcurrentHashMap) поведение более подходящее для многопоточных сценариев, но точная семантика удаления и видимость зависят от конкретной реализации.

Типичное применение: быстро удалить все элементы, чтобы переиспользовать экземпляр множества без выделения новой структуры или чтобы очистить представление коллекции, связанной с другой структурой (например, удалить все записи из карты через map.keySet().clear()).

Короткие примеры использования

Пример 1. Обычное очищение HashSet:

import java.util.*;

class Example1 {
    public static void main(String[] args) {
        Set set = new HashSet<>(Arrays.asList("a", "b", "c"));
        set.clear();
        System.out.println(set.size());
        System.out.println(set.isEmpty());
    }
}
0
true

Пример 2. Попытка очистить неизменяемое множество:

import java.util.*;

class Example2 {
    public static void main(String[] args) {
        Set immutable = Set.of("x", "y");
        immutable.clear(); // UnsupportedOperationException
    }
}
Exception in thread "main" java.lang.UnsupportedOperationException
    at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
    at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.clear(ImmutableCollections.java:148)
    ...

Пример 3. Очистка представления ключей карты влияет на карту:

import java.util.*;

class Example3 {
    public static void main(String[] args) {
        Map map = new HashMap<>();
        map.put("a", 1);
        map.put("b", 2);
        map.keySet().clear();
        System.out.println(map.size());
    }
}
0

Пример 4. Модификация во время итерации вызывает исключение у большинства реализаций:

import java.util.*;

class Example4 {
    public static void main(String[] args) {
        Set set = new HashSet<>(Arrays.asList("a","b","c"));
        for (String s : set) {
            if (s.equals("b")) {
                set.clear(); // ConcurrentModificationException
            }
        }
    }
}
Exception in thread "main" java.util.ConcurrentModificationException
    at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1577)
    at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1600)
    at java.base/java.util.HashSet$Itr.next(HashSet.java:157)
    ...

Пример 5. Очистка конкурентного множества безопасна для параллельного использования в большинстве случаев:

import java.util.*;
import java.util.concurrent.*;

class Example5 {
    public static void main(String[] args) {
        Set cs = new ConcurrentSkipListSet<>(Arrays.asList("a","b","c"));
        cs.clear();
        System.out.println(cs.isEmpty());
    }
}
true

Похожие методы в Java и критерии выбора

  • removeAll(Collection c) - удаляет все элементы, содержащиеся в переданной коллекции. Используется для удаления определённого поднабора, а не всех элементов.
  • removeIf(Predicate) - удаляет элементы по условию; полезно, когда нужно удалить часть элементов без полного очищения.
  • iterator().remove() - удаление текущего элемента во время итерации; безопасно для последовательной модификации по одному элементу.
  • retainAll(Collection c) - оставляет только элементы, присутствующие в переданной коллекции; применяется для пересечения множеств.
  • Присваивание новой коллекции (например, set = new HashSet<>()) - альтернатива, когда нет ссылок, которые требуется очистить, и приемлемо переназначение переменной. При наличии других ссылок на исходный объект это не приведёт к их очистке.

Выбор метода зависит от задачи: полная очистка - clear(), фильтрация - removeIf или removeAll, аккуратное удаление во время итерации - через итератор.

Аналоги в других языках и отличия

  • JavaScript: Set.prototype.clear() - удаляет все элементы, возвращает undefined. Пример:
    const s = new Set(['a','b']);
    s.clear();
    console.log(s.size);
    0
  • Python: set.clear() - удаляет все элементы, возвращает None. Пример:
    s = {1,2,3}
    s.clear()
    print(len(s), s)
    0 set()
  • C#: HashSet<T>.Clear() - удаляет все элементы, возвращает void. Пример:
    var s = new HashSet{"a","b"};
    s.Clear();
    Console.WriteLine(s.Count);
    0
  • Kotlin: MutableSet.clear() - поведение аналогично Java. Пример:
    val s = mutableSetOf("a","b")
    s.clear()
    println(s.size)
    0
  • Go: в языке нет встроенного множества, обычно используется map[T]struct{}. Очистка выполняется переназначением или итерацией с удалением. Пример:
    m := map[string]struct{}{"a":{},"b":{}}
    // очистка
    m = make(map[string]struct{})
    fmt.Println(len(m))
    0
  • Lua: таблица как множество, очистка - установка nil для ключей или новая таблица. Пример:
    s = {a=true, b=true}
    for k in pairs(s) do s[k] = nil end
    print(next(s)) -- nil when empty
    
    nil
  • PHP: нет прямого аналога Set в стандартных массивах; очистка массива - $arr = []; или unset. Для SplObjectStorage есть метод removeAll и другие средства.
  • SQL: для удаления всех строк из таблицы используются DELETE FROM table или TRUNCATE TABLE; отличие - операции на уровне БД, не памяти приложения.

Отличия от Java: в большинстве языков метод очистки доступен и семантически схож; различия касаются возврата значения, потокобезопасности и того, является ли структура изменяемой по умолчанию. В Go и Lua очистка чаще осуществляется переназначением контейнера, поскольку структуры реализации отличаются.

Типичные ошибки при использовании

  • UnsupportedOperationException: попытка вызвать clear() у неизменяемого множества. Пример:
    Set s = Collections.unmodifiableSet(new HashSet<>(List.of("a")));
    s.clear();
    Exception in thread "main" java.lang.UnsupportedOperationException
        at java.base/java.util.Collections$UnmodifiableCollection.clear(Collections.java:XXXX)
        ...
    
  • ConcurrentModificationException: модификация коллекции во время итерации стандартными итераторами. Пример:
    Set s = new HashSet<>(Arrays.asList(1,2,3));
    for (Integer i : s) {
        s.clear();
    }
    
    Exception in thread "main" java.util.ConcurrentModificationException
        at ...
    
  • Неправильное ожидание о ссылках: присвоение новой коллекции переменной (например, set = new HashSet<>()) не очистит другие ссылки на прежний объект. В таких случаях clear() корректно очищает сам объект и все ссылки на него увидят изменения.
  • Проблемы многопоточности: вызов clear() без внешней синхронизации на несинхронизированных реализациях может привести к неконсистентному состоянию в параллельном окружении. Для общего HashSet требуется внешняя синхронизация или использование Concurrent-реализаций.

Изменения и совместимость

Метод clear() присутствует в интерфейсе Collection с ранних версий Java (начиная с версии коллекций, Java 1.2). За последние релизы языка сама сигнатура и базовая семантика не менялись. Новые релизы добавляли неизменяемые фабрики коллекций (Set.of(...) в Java 9), которые делают вызов clear() неприемлемым и приводят к UnsupportedOperationException. Также появились удобные методы для пакетной модификации, например removeIf, предоставляющие альтернативы частичным очисткам.

Расширенные и малоизвестные сценарии

1) Очистка диапазона в TreeSet через view (subSet):

Пример java
import java.util.*;

class Adv1 {
    public static void main(String[] args) {
        TreeSet t = new TreeSet<>(Arrays.asList(1,2,3,4,5));
        t.subSet(2, 5).clear(); // удалит 2,3,4
        System.out.println(t);
    }
}
[1, 5]

Пояснение: subSet возвращает представление части множества. Очистка представления удаляет соответствующие элементы во всём наборе.

2) Удаление по условию, эквивалентное частичной очистке:

Пример java
import java.util.*;

class Adv2 {
    public static void main(String[] args) {
        Set s = new HashSet<>(Arrays.asList("apple","banana","cherry"));
        s.removeIf(str -> str.startsWith("b"));
        System.out.println(s);
    }
}
[apple, cherry]

Пояснение: вместо полного clear() применяется фильтрация с помощью предиката.

3) Очистка карты через ключи с условием:

Пример java
import java.util.*;

class Adv3 {
    public static void main(String[] args) {
        Map map = new HashMap<>();
        map.put("a",1); map.put("b",2); map.put("c",3);
        // удалить все записи, где значение меньше 3
        map.keySet().removeIf(k -> map.get(k) < 3);
        System.out.println(map);
    }
}
{c=3}

Пояснение: применение removeIf к keySet() позволяет удалять записи карты по условию без явного перебора entrySet().

4) Корректная очистка при итерации: использование итератора для безопасного удаления по одному элементу:

Пример java
import java.util.*;

class Adv4 {
    public static void main(String[] args) {
        Set s = new HashSet<>(Arrays.asList(1,2,3,4));
        Iterator it = s.iterator();
        while (it.hasNext()) {
            Integer v = it.next();
            if (v % 2 == 0) {
                it.remove(); // безопасно
            }
        }
        System.out.println(s);
    }
}
[1, 3]

5) Переиспользование экземпляра и управление памятью: при очень больших множествах вызов clear() удаляет элементы, но внутренние структуры (например, массив бакетов в HashSet) могут остаться прежнего размера. Для снижения занимаемой памяти предпочтительнее создать новый объект, если требуется уменьшить внутреннюю ёмкость.

6) Поведение в многопоточном окружении: у ConcurrentSkipListSet и у наборов, созданных через ConcurrentHashMap.newKeySet(), вызов clear() работает для параллельного использования, но не обеспечивает глобальной атомарности для внешних операций. В сценариях, где требуется полная атомарность видимости, может потребоваться внешняя синхронизация или блокировка вышеуровневой логики.

7) Очистка и мониторинг: при реализации кэшей или структур с наблюдателями (listeners) важно учитывать, вызываются ли подписчики при массовых операциях clear() - у некоторых обёрток и реализаций коллбэки могут быть вызваны для каждого удаления.

джава Set.clear() function comments

En
Set.clear() Удаляет все элементы из набора