Set.size(): примеры (JAVA)
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()); // вызовет NPEException 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() важно.
// Пример: подсчёт уникальных элементов через 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
// Пример: использование 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
// Пример: ручной счётчик размера для высокопроизводительных конкурентных вставок
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().