Stream.mapToInt: примеры (JAVA)
Stream.mapToInt(ToIntFunction super T> mapper): IntStreamОписание и сигнатура
Метод mapToInt интерфейса java.util.stream.Stream преобразует поток ссылочных объектов в примитивный поток IntStream. Обычно используется, когда требуется получить примитивные значения int из объектов для вычислений, агрегаций или для экономии на бокcинге.
Сигнатура:
IntStream mapToInt(ToIntFunction<? super T> mapper)
Аргументы:
mapper- функциональный интерфейсToIntFunction<T>. Принимает элемент типаTи возвращаетint. Может быть задан лямбдой или ссылкой на метод.
Возвращаемое значение:
IntStream- ленивый поток примитивныхint. Поддерживает типичные операции примитивного стрима:sum(), average(), toArray(), map(), boxed(), collect()и т.д.
Поведение и особенности:
- Преобразование не изменяет порядок элементов, если исходный поток упорядочен, и операции не нарушают порядок.
- Применение
mapToIntустраняет авто-боксинг при дальнейших числовых операциях, что уменьшает накладные расходы и алокации. - Если
mapperравенnullили бросает исключение (включаяNullPointerExceptionпри обращении к полю null-объекта), выполнение бросит соответствующее исключение при стадии терминальной операции. - Лямбда не может объявлять проверяемые исключения без обработки; это приведет к ошибке компиляции.
Короткие практические примеры
Пример 1. Преобразование списка строк в длины строк:
List<String> names = List.of("Anna", "Boris", "Cat");
IntStream ints = names.stream().mapToInt(String::length);
int[] arr = ints.toArray();
System.out.println(Arrays.toString(arr));
[4, 5, 3]
Пример 2. Извлечение числового поля из объектов:
record Item(String name, int qty) {}
List<Item> items = List.of(new Item("A", 5), new Item("B", 12));
int sum = items.stream().mapToInt(Item::qty).sum();
System.out.println(sum);
17
Пример 3. Метод-референция и лямбда-версия:
// метод-ссылка
IntStream s1 = Stream.of("x","yy").mapToInt(String::length);
// лямбда
IntStream s2 = Stream.of("x","yy").mapToInt(s -> s.length());
System.out.println(s1.sum());
System.out.println(s2.sum());
3 3
Пример 4. Параллельный поток (порядок сохраняется для упорядоченных источников):
int sum = IntStream.range(0, 1_000_000)
.parallel()
.map(i -> i * 2)
.sum();
System.out.println(sum);
1999998000000
Похожие методы в Java
map(Stream<T>>)mapToLongиmapToDoubleflatMapToIntboxed()наIntStream
Возвращает объектный Stream. Используется если результат не примитив int или требуется сохранить тип-объект (например, Integer).
Аналогичные примитивные специализации для long и double. Предпочтение зависит от требуемой точности и диапазона значений.
Используется при необходимости раскрыть вложенные коллекции в единый поток int (пример: список массивов чисел).
Преобразует IntStream обратно в Stream<Integer>, когда требуется собрать объекты или использовать обобщенные коллекции.
Аналоги в других языках и отличия
Кратко о ключевых отличиях: в Java Streams присутствует ленивость, примитивные специализации и интеграция с параллелизмом. Во многих других языках операции подобны, но обычно возвращают коллекции, не имеют примитивных оптимизаций или ленивости по умолчанию.
PHP:
$arr = ['one', 'three', 'a'];
$res = array_map('strlen', $arr);
print_r($res);
Array
(
[0] => 3
[1] => 5
[2] => 1
)
JavaScript:
const arr = ['one','three','a'];
const res = arr.map(s => s.length);
console.log(res);
[3, 5, 1]
Python:
arr = ['one','three','a']
res = list(map(len, arr))
print(res)
[3, 5, 1]
SQL (пример в SELECT):
SELECT LENGTH(name) AS len FROM users;
-- возвращает столбец len с длинами строк для каждой строки таблицы
C# (LINQ):
var arr = new[] { "one", "three", "a" };
var res = arr.Select(s => s.Length).ToArray();
Console.WriteLine(string.Join(",", res));
3,5,1
Go (Golang):
arr := []string{"one","three","a"}
res := make([]int, len(arr))
for i,s := range arr { res[i] = len(s) }
fmt.Println(res)
[3 5 1]
Kotlin:
val arr = listOf("one","three","a")
val res = arr.map { it.length }
println(res)
[3, 5, 1]
Lua:
local arr = { 'one','three','a' }
local res = {}
for i,s in ipairs(arr) do res[i] = #s end
for i,v in ipairs(res) do print(v) end
3 5 1
Отличия:
- Во многих языках операции выполняются немедленно и возвращают коллекцию.
- Java предоставляет специализированные примитивные потоки (
IntStream) для уменьшения бокcинга, чего обычно нет в динамических языках. - Параллельные операции в Java Streams реализованы на уровне API; в других языках параллелизм требует явных средств.
Типичные ошибки и их проявления
1) Передача null вместо mapper:
Stream.of(1,2,3).mapToInt(null).sum();
Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:233)
at java.base/java.util.stream.ReferencePipeline.mapToInt(ReferencePipeline.java:...)
...
2) Наличие null-элемента и обращение к методу/полю в mapper:
List<String> l = Arrays.asList("a", null);
int[] a = l.stream().mapToInt(s -> s.length()).toArray();
Exception in thread "main" java.lang.NullPointerException
at ... (в лямбде s.length())
3) Попытка пробросить проверяемое исключение из лямбды:
Stream.of("x").mapToInt(s -> { throw new IOException(); });
Compilation error: unreported exception java.io.IOException; must be caught or declared to be thrown
4) Потеря информации при преобразовании типов (логика приложения): если mapper возвращает int из long или BigInteger, возможна потеря данных - это не проверяется компилятором.
История и изменения
Метод mapToInt введен в Java 8 вместе со Streams API. С тех пор интерфейс не претерпел значимых изменений. Поддержка примитивных потоков оставалась стабильной в последующих релизах Java. В целом улучшения в Stream API касались производительности и небольших дополнений в утилитах, но сигнатура mapToInt не менялась.
Расширенные и необычные сценарии
1) Использование IntSummaryStatistics для сбора статистики:
record Person(String name, int age) {}
List<Person> people = List.of(new Person("A", 20), new Person("B", 35), new Person("C", 35));
IntSummaryStatistics stats = people.stream().mapToInt(Person::age).summaryStatistics();
System.out.println(stats);
IntSummaryStatistics{count=3, sum=90, min=20, average=30.000000, max=35}
2) Преобразование вложенных коллекций в единый IntStream (flatMapToInt):
List<int[]> data = List.of(new int[]{1,2}, new int[]{3});
int sum = data.stream().flatMapToInt(Arrays::stream).sum();
System.out.println(sum);
6
3) Получение индексированного потока значений (эмуляция with index):
List<String> list = List.of("a","bb","ccc");
int[] lengthsWithIndex = IntStream.range(0, list.size())
.map(i -> i + list.get(i).length())
.toArray();
System.out.println(Arrays.toString(lengthsWithIndex));
[1, 3, 5]
4) Применение в параллельных агрегациях с последующим boxed и группировкой:
List<String> arr = IntStream.range(0,1000).mapToObj(i -> "s"+i).collect(Collectors.toList());
Map<Integer, Long> countByLen = arr.parallelStream()
.mapToInt(String::length)
.boxed()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
System.out.println(countByLen);
{2=1000} // примерный вывод в зависимости от данных
5) Сбор в массив примитивов для межоперационного обмена с нативным кодом:
int[] primitives = objects.stream().mapToInt(o -> o.getId()).toArray();
// primitives можно передать в JNI или записать в ByteBuffer
[примитивный массив int]
6) Генерация битовых масок и последующие битовые операции:
List<Boolean> flags = List.of(true,false,true);
int mask = flags.stream().mapToInt(b -> b ? 1 : 0)
.reduce(0, (acc, bit) -> (acc << 1) | bit);
System.out.println(Integer.toBinaryString(mask));
101
7) Обработка Unicode code points в строках (подсчёт кодовых позиций):
String s = "????a"; // символ вне BMP + обычная буква
int cpCount = s.codePoints().mapToInt(i -> 1).sum();
System.out.println(cpCount);
2
8) Комбинация с Collectors.summarizingInt через mapToInt для удобной агрегации после boxed:
IntSummaryStatistics st = people.stream()
.collect(Collectors.summarizingInt(Person::age));
System.out.println(st.getAverage());
30.0
Пояснения: в продвинутых сценариях mapToInt используется совместно с примитивными методами IntStream для экономии памяти и повышения производительности. Для группировок и операций, требующих объектов, выполняется boxed().