Stream.mapToInt: примеры (JAVA)

Преобразование элементов потока в int
Раздел: Потоки данных (Stream API) - промежуточные операции
Stream.mapToInt(ToIntFunction 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>>)
  • Возвращает объектный Stream. Используется если результат не примитив int или требуется сохранить тип-объект (например, Integer).

  • mapToLong и mapToDouble
  • Аналогичные примитивные специализации для long и double. Предпочтение зависит от требуемой точности и диапазона значений.

  • flatMapToInt
  • Используется при необходимости раскрыть вложенные коллекции в единый поток int (пример: список массивов чисел).

  • boxed() на IntStream
  • Преобразует 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 для сбора статистики:

Пример java
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):

Пример java
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):

Пример java
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 и группировкой:

Пример java
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) Сбор в массив примитивов для межоперационного обмена с нативным кодом:

Пример java
int[] primitives = objects.stream().mapToInt(o -> o.getId()).toArray();
// primitives можно передать в JNI или записать в ByteBuffer
[примитивный массив int]

6) Генерация битовых масок и последующие битовые операции:

Пример java
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 в строках (подсчёт кодовых позиций):

Пример java
String s = "????a"; // символ вне BMP + обычная буква
int cpCount = s.codePoints().mapToInt(i -> 1).sum();
System.out.println(cpCount);
2

8) Комбинация с Collectors.summarizingInt через mapToInt для удобной агрегации после boxed:

Пример java
IntSummaryStatistics st = people.stream()
    .collect(Collectors.summarizingInt(Person::age));
System.out.println(st.getAverage());
30.0

Пояснения: в продвинутых сценариях mapToInt используется совместно с примитивными методами IntStream для экономии памяти и повышения производительности. Для группировок и операций, требующих объектов, выполняется boxed().

джава Stream.mapToInt function comments

En
Stream.mapToInt Returns an IntStream consisting of the results of applying the given function to the elements