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

Информация о размере множества в Java
Раздел: Коллекции, Set
Set.size(): int

Описание метода Set.size()

В Java метод size() определён в интерфейсе Collection<E> и возвращает количество элементов множества. Сигнатура метода в общем виде: int size(). Метод не принимает аргументов и всегда возвращает целое значение типа int, равное числу элементов, содержащихся в объекте Set.

Поведение и особенности:

  • Возвращаемое значение - текущее количество элементов. Для пустого множества возвращается 0.
  • Поддерживается всеми реализациями Set (HashSet, TreeSet, LinkedHashSet, EnumSet и т.д.).
  • Сложность выполнения зависит от конкретной реализации. Для большинства реализаций (HashSet, TreeSet) получение размера выполняется за константное время, поскольку размер хранится отдельно. Для некоторых реализаций, особенно тех, что основаны на конкурентных структурах, вычисление размера может быть линейным или дорогостоящим при параллельных изменениях.
  • Метод возвращает int, поэтому при концептуально возможном числе элементов, превышающем Integer.MAX_VALUE, возникает риск некорректного представления размера. Для карт (ConcurrentHashMap) существует метод с long-возвратом (см. разделы ниже).
  • Вызов size() на null-ссылке приведёт к NullPointerException до входа в метод.
  • При одновременных изменениях коллекции в разных потоках результат может отражать момент времени вызова и не гарантирует атомарность вместе с другими операциями; для согласованного снимка требуется внешняя синхронизация или специализированные методы.

Когда целесообразно применять:

  • При необходимости узнать текущее количество уникальных элементов в множестве.
  • Для проверки на пустоту удобно сравнить с нулём, но для читаемости и потенциальной оптимальности предпочтение иногда отдаётся isEmpty().

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

Ниже приведены простые варианты вызова size() для различных реализаций Set. В каждом примере сначала код, затем ожидаемый вывод.

// Пример 1: HashSet
import java.util.*;
class Example1 {
  public static void main(String[] args) {
    Set<String> s = new HashSet<>();
    s.add("a");
    s.add("b");
    s.add("a"); // дубликат не увеличит размер
    System.out.println(s.size());
  }
}
2
// Пример 2: TreeSet
import java.util.*;
class Example2 {
  public static void main(String[] args) {
    Set<Integer> s = new TreeSet<>(Arrays.asList(3,1,2));
    System.out.println(s.size());
  }
}
3
// Пример 3: EnumSet
import java.util.*;
enum Color { RED, GREEN, BLUE }
class Example3 {
  public static void main(String[] args) {
    Set<Color> s = EnumSet.of(Color.RED, Color.BLUE);
    System.out.println(s.size());
  }
}
2
// Пример 4: Concurrent set на базе ConcurrentHashMap
import java.util.*;
import java.util.concurrent.*;
class Example4 {
  public static void main(String[] args) {
    Set<String> s = ConcurrentHashMap.newKeySet();
    s.add("x");
    s.add("y");
    System.out.println(s.size());
  }
}
2

Похожие методы в Java

Сравнение и краткие рекомендации по близким API:

  • Collection.size() - общий контракт, от которого наследует Set.size(). Применимо ко всем коллекциям.
  • Collection.isEmpty() - возвращает boolean, чаще предпочтительно для проверки пустоты, так как выражение isEmpty() читается очевиднее, а некоторые реализации оптимизированы под этот вызов.
  • Map.size() - для отображений; сходная семантика, возвращает количество пар ключ-значение.
  • ConcurrentHashMap.mappingCount() - возвращает long и может быть полезен при учёте большого количества элементов в конкурентных картах; отличается от size() типом возвращаемого значения и реализованными оптимизациями для масштабируемости.
  • Stream.count() - если набор получается как поток, подсчёт элементов выполняется методом count() и возвращает long. При обработке больших объёмов данных через Stream предпочтение может отдаваться именно ему.

В целом, для проверки пустоты лучше использовать isEmpty(), а для получения количества элементов - size(). Для больших или конкурентных структур иногда имеет смысл использовать специализированные методы с возвращением long или поддерживающие более точную семантику при параллелизме.

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

Краткие соответствия и отличия от Java:

  • PHP: count($set) - для массивов и объектов, реализующих Countable. Возвращает int. Пример:
    $s = array_unique([1,2,1]);
    echo count($s);
    2
  • JavaScript: у Set есть свойство size (не метод). Возвращает число элементов.
    const s = new Set(['a','b','a']);
    console.log(s.size);
    2
  • Python: встроенная функция len() для множеств. Возвращает int (здесь неограниченный целочисленный тип Python).
    s = set(['a','b','a'])
    print(len(s))
    2
  • SQL: агрегатная функция COUNT(), часто с DISTINCT для подсчёта уникальных значений в столбце.
    SELECT COUNT(DISTINCT col) FROM table;
    (число уникальных значений)
  • C#: у HashSet<T> есть свойство Count, возвращает int.
    var s = new HashSet<int>{1,2,1};
    Console.WriteLine(s.Count);
    2
  • Lua: нет встроенного типа set; обычно множества моделируются через таблицу и используется pairs и ручной подсчёт или поддерживается счётчик. Прямого аналога size() нет.
    local s = { a=true, b=true }
    local cnt = 0
    for k,v in pairs(s) do cnt = cnt + 1 end
    print(cnt)
    2
  • Go: множества моделируются через map[T]struct{}. Размер получается через len(map).
    s := map[string]struct{}{"a":{}, "b":{}}
    fmt.Println(len(s))
    2
  • Kotlin: у Set есть свойство size, аналогичное Java по семантике.
    val s = setOf("a","b","a")
    println(s.size)
    2

Отличия в основном в типе возвращаемого значения (в Java и многих языках это int, в Python - произвольная точность), в синтаксисе (свойство vs метод) и в доступных оптимизациях для конкурентных коллекций.

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

  • NullPointerException: попытка вызвать size() у null-ссылки. Пример:
    Set<String> s = null;
    System.out.println(s.size()); // вызовет NPE
    Exception in thread "main" java.lang.NullPointerException
        at Example.main(Example.java:...)
  • Ожидание атомарности при конкурентных изменениях: получение размера не делает другие операции атомарными. Если структуру изменяют другие потоки, значение может быстро устаревать или быть непоследовательным.
  • Производительность: у некоторых реализаций вычисление размера может быть дорогостоящим (например, при распределённых или неблокирующих структурах), поэтому частые вызовы в горячих циклах могут повлиять на производительность.
  • Переполнение логики: ожидание большего диапазона значений, чем помещается в int. Для очень больших наборов может потребоваться использовать структуры с подсчётом в long у карты или специализированные счётчики.

Изменения поведения в последних версиях Java

Сам метод size() как часть интерфейса Collection не претерпевал значительных изменений в семантике в последних релизах. Основные эволюции касались сопутствующих API:

  • Появление фабрик коллекций в Java 9 (Set.of(...)) не изменяет поведение size(), но добавляет неизменяемые реализации.
  • В ConcurrentHashMap и связанных структурах добавлены или улучшены методы для получения более точной или подходящей по типу информации о размере (например, методы, возвращающие long), что полезно при работе с очень большими или конкурентными наборами.

Вывод: непосредственно менять способ вызова size() не требуется, но при работе с новыми реализациями коллекций стоит изучать сопутствующие методы для корректного и эффективного подсчёта элементов.

Расширенные и нечастые варианты применения

Несколько продвинутых сценариев, где внимание к size() важно.

Пример java
// Пример: подсчёт уникальных элементов через Stream и сравнение с size()
import java.util.*;
import java.util.stream.*;
class Adv1 {
  public static void main(String[] args) {
    List<String> list = Arrays.asList("a","b","a","c");
    long distinctCount = list.stream().distinct().count(); // возвращает long
    Set<String> set = new HashSet<>(list);
    System.out.println(distinctCount);
    System.out.println(set.size());
  }
}
3
3
Пример java
// Пример: использование ConcurrentHashMap.mappingCount для большого количества элементов
import java.util.concurrent.*;
class Adv2 {
  public static void main(String[] args) {
    ConcurrentHashMap<String, Boolean> map = new ConcurrentHashMap<>();
    for (int i = 0; i < 1_000_000; i++) map.put(Integer.toString(i), Boolean.TRUE);
    System.out.println("map.mappingCount(): " + map.mappingCount()); // long
    Set<String> set = map.keySet(Boolean.TRUE);
    System.out.println("set.size(): " + set.size()); // int (может быть медленнее и ограничен int)
  }
}
map.mappingCount(): 1000000
set.size(): 1000000
Пример java
// Пример: ручной счётчик размера для высокопроизводительных конкурентных вставок
import java.util.concurrent.*;
class Adv3 {
  public static void main(String[] args) throws InterruptedException {
    Set<Integer> s = ConcurrentHashMap.newKeySet();
    LongAdder counter = new LongAdder();

    Runnable addTask = () -> {
      for (int i = 0; i < 1000; i++) {
        if (s.add(i)) counter.increment();
      }
    };

    Thread t1 = new Thread(addTask);
    Thread t2 = new Thread(addTask);
    t1.start(); t2.start();
    t1.join(); t2.join();

    System.out.println("set.size(): " + s.size());
    System.out.println("counter.sum(): " + counter.sum());
  }
}
set.size(): 1000
counter.sum(): 1000

Пояснения:

  • При подсчёте через Stream применяется count() и возвращается long, что удобно для больших потоков.
  • Для конкурентных коллекций использование специализированных методов (например, mappingCount()) и внешних счётчиков (LongAdder) помогает получить корректную и масштабируемую метрику.
  • В некоторых сценариях хранение и обновление отдельного счётчика при добавлении/удалении даёт более предсказуемую и быструю статистику, чем многократные вызовы size().

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

En
Set.size() Возвращает количество элементов