Stream.count: примеры (JAVA)
Stream.count: longОписание метода
Метод count() интерфейса java.util.stream.Stream и примитивных потоков (IntStream, LongStream, DoubleStream) представляет собой терминальную операцию, возвращающую количество элементов в потоке. Возвращаемый тип у всех реализаций - long. Метод не принимает аргументов.
Краткие характеристики:
- Тип вызова: терминальная операция.
- Параметры: отсутствуют.
- Возвращаемое значение: число элементов типа long.
- Поведение для пустого потока: возвращается 0.
- Параллельность: корректно работает с последовательными и параллельными потоками; реализация использует разбиение источника через Spliterator.
- Переиспользование: после терминальной операции поток становится недействительным; повторный вызов операций на том же объекте приведет к исключению.
- Работа с бесконечными потоками: если поток бесконечен и не применяется ограничение (limit), вызов будет блокирующим и не завершится.
Особенности реализации и взаимодействия с API:
- Для коллекций получение размера через Collection.size() обычно быстрее и константно по времени, тогда как stream().count() производит обход и имеет линейную сложность.
- Альтернативой внутри Stream API является использование Collectors.counting() в собранных результатах, особенно при группировке.
- count() гарантирует точный подсчет элементов, как они проходит через весь цепочку промежуточных операций (фильтрация, маппинг, distinct и т. п.).
Примеры простого применения
Ниже приведены простые случаи вызова метода count() с выводом кода и результата.
1) Подсчет элементов в потоке объектов
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
long c = Stream.of("a", "b", "c").count();
System.out.println(c);
}
}
3
2) Подсчет с фильтрацией
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
long evens = Stream.of(1, 2, 3, 4, 5, 6)
.filter(n -> n % 2 == 0)
.count();
System.out.println(evens);
}
}
3
3) Примитивный поток и диапазон
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
long cnt = IntStream.rangeClosed(1, 100).count();
System.out.println(cnt);
}
}
100
4) Параллельный подсчет
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
long cnt = Arrays.asList(1,2,3,4,5,6,7,8)
.parallelStream()
.filter(x -> x > 4)
.count();
System.out.println(cnt);
}
}
4
Похожие методы в Java
- Collection.size() - предоставляет размер коллекции за константное время для большинства реализаций коллекций. Предпочтительнее, если доступен объект Collection и требуется просто его размер.
- Collectors.counting() - используется в комбинации с collect(), полезен при группировке: groupingBy(..., Collectors.counting()). Возвращает Long для каждой группы.
- Stream.reduce() или mapToLong(...).sum() - альтернативные способы подсчета, но менее выразительны и чаще избыточны по сравнению с count().
Выбор между этими вариантами определяется контекстом: для простого подсчета элементов в коллекции - size(), для подсчета с группировкой - Collectors.counting(), для подсчета на потоке без сохранения в коллекцию - count().
Аналоги в других языках
Краткие отличия и примеры подсчета элементов в популярных языках.
JavaScript - массивы и итераторы
// массив
const arr = [1,2,3];
console.log(arr.length);
// Итератор с фильтром
const count = [...arr].filter(x => x>1).length;
console.log(count);
3 2
Python - len и подсчет для итераторов
# список
lst = [1,2,3]
print(len(lst))
# генератор
cnt = sum(1 for x in lst if x%2==0)
print(cnt)
3 1
PHP - функция count()
$arr = [1,2,3];
echo count($arr);
3
SQL - агрегатная функция COUNT
SELECT COUNT(*) FROM users WHERE active = 1;
-- возвращает количество строк, соответствующих условию
C# - LINQ Count()
using System.Linq;
var arr = new[] {1,2,3,4};
Console.WriteLine(arr.Count());
Console.WriteLine(arr.Count(x => x%2==0));
4 2
Kotlin - count() и size
val list = listOf(1,2,3)
println(list.size)
println(list.count { it%2==0 })
3 1
Go - len для срезов; для каналов требуется обход
slice := []int{1,2,3}
fmt.Println(len(slice))
// для канала
cnt := 0
for v := range ch { cnt++ }
3 // cnt подсчитывается во время чтения канала
Lua - оператор # для массивоподобных таблиц
local t = {1,2,3}
print(#t)
3
Отличия от Java: в большинстве языков для коллекций есть константный способ получить размер (length, len, size), тогда как подсчет по потоку/итератору обычно требует обхода. В некоторых языках (Kotlin, C#) присутствуют удобные функции с предикатом, аналогичные Stream.filter(...).count().
Типичные ошибки и нюансы
- IllegalStateException: повторный вызов операций на одном и том же объекте Stream. Пример:
Streams = Stream.of("a", "b"); long c1 = s.count(); long c2 = s.count(); // выбросит IllegalStateException: stream has already been operated upon or closed Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed at ...
- NullPointerException при вызове count() на null-ссылке вместо самого потока. Пример:
Streams = null; long c = s.count(); // NPE Exception in thread "main" java.lang.NullPointerException at ...
- Блокировка при работе с бесконечными потоками без ограничения. Пример:
long c = Stream.iterate(0, n -> n + 1).count(); // никогда не завершится// программа будет висеть, подсчет не завершится
- Производительность: использование stream().count() для уже доступных коллекций менее эффективно, чем Collection.size() для тех коллекций, где размер хранится.
- Переполнение: возвращаемый тип long; если гипотетически требуется подсчитать больше, чем Long.MAX_VALUE элементов, переполнение не проверяется явно.
Изменения в API
Метод count() введен в Java 8 вместе со Stream API и с тех пор сохраняет прежнее поведение: отсутствие параметров, возвращение long и терминальность. В последующих версиях Java существенных изменений в сигнатуре или семантике count() не происходило. Дополнялись смежные механизмы Stream API, улучшалась производительность и поддержка параллельного выполнения, но контракт метода остался прежним.
Расширенные и нестандартные примеры
1) Подсчет элементов по группам через groupingBy и Collectors.counting()
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
List names = Arrays.asList("Anna", "Bob", "Alice", "Bob");
Map counts = names.stream()
.collect(Collectors.groupingBy(n -> n, Collectors.counting()));
System.out.println(counts);
}
}
{Alice=1, Bob=2, Anna=1}
2) Подсчет уникальных элементов
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
long distinct = Stream.of(1,2,2,3,3,3)
.distinct()
.count();
System.out.println(distinct);
}
}
3
3) Безопасный подсчет по ленивому потоку с ограничением (для потенциально бесконечных источников)
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
long cnt = Stream.iterate(0, n -> n + 1)
.limit(1000)
.filter(n -> n % 7 == 0)
.count();
System.out.println(cnt);
}
}
143
4) Подсчет с использованием параллельности и пользовательского источника
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.StreamSupport;
public class Main {
public static void main(String[] args) {
Spliterator sp = Spliterators.spliteratorUnknownSize(
java.util.stream.Stream.of(1,2,3,4,5,6).iterator(), 0);
long cnt = StreamSupport.stream(sp, true) // параллельный
.filter(x -> x > 2)
.count();
System.out.println(cnt);
}
}
4
5) Подсчет элементов после сложной трансформации (map + flatMap)
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
long cnt = Arrays.asList("a,b", "c", "d,e,f").stream()
.flatMap(s -> Arrays.stream(s.split(",")))
.count();
System.out.println(cnt);
}
}
6
6) Подсчет с ранним завершением с помощью limit для экономии ресурсов
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
long cnt = IntStream.range(1, Integer.MAX_VALUE)
.filter(n -> n % 1000000 == 0)
.limit(5) // гарантирует, что count завершится
.count();
System.out.println(cnt);
}
}
5
Пояснения: в продвинутых сценариях чаще используется комбинация фильтрации, distinct, limit и группировок. При работе с большими данными предпочтение отдается параллельным потокам при этом учитывается стоимость разделения источника и безопасность данных.