Почему я не могу сопоставить целые числа со строками при потоковой передаче из массива?

94

Этот код работает (взят из Javadoc):

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
String commaSeparatedNumbers = numbers.stream()
    .map(i -> i.toString())
    .collect(Collectors.joining(", "));

Этот не может быть скомпилирован:

int[] numbers = {1, 2, 3, 4};
String commaSeparatedNumbers = Arrays.stream(numbers)
    .map((Integer i) -> i.toString())
    .collect(Collectors.joining(", "));

IDEA сообщает мне, что у меня есть «несовместимый тип возвращаемого значения String в лямбда-выражении».

Зачем ? И как это исправить?

Дени Сегюре
источник

Ответы:

121

Arrays.stream(int[])создает IntStream, а не Stream<Integer>. Таким образом , при сопоставлении с объектом вам нужно вызвать, mapToObjа не просто .mapint

Это должно работать, как ожидалось:

String commaSeparatedNumbers = Arrays.stream(numbers)
    .mapToObj(i -> ((Integer) i).toString()) //i is an int, not an Integer
    .collect(Collectors.joining(", "));

который вы также можете написать:

String commaSeparatedNumbers = Arrays.stream(numbers)
    .mapToObj(Integer::toString)
    .collect(Collectors.joining(", "));
ассилий
источник
3
В чем разница между IntStreamи Stream<Integer>?
Флориан Маргейн,
8
@FlorianMargaine An IntStream- это потоковая специализация для примитивных intзначений. A Stream<Integer>- это просто Integerобъекты Stream .
Alexis C.
2
@FlorianMargaine IntStream- это поток или примитивы (ints), тогда Steram<Integer>как это поток объектов. У примитивных потоков есть специальные методы по соображениям производительности.
assylias
7
IntStream.mapToObjожидает IntFunction, функция, которая потребляет intзначение, поэтому  .mapToObj((Integer i) -> i.toString())не работает. В любом случае это не рекомендуется, поскольку содержит ненужное преобразование из intв Integer. Напротив, он .mapToObj(Integer::toString)прекрасно работает, поскольку вызывает staticметод Integer.toString(int). Обратите внимание, что это отличается от вызова .map(Integer::toString)a,  Stream<Integer>поскольку последний не компилируется из-за неоднозначности.
Holger
1
не @cic: нет, вызов .map(Integer::toString)на Stream<Integer>действительно неоднозначна , поскольку оба, .map(i->i.toString())и .map(i->Integer.toString(i))являются действительными. Но это легко решить с помощью .map(Object::toString).
Holger
19

Arrays.stream(numbers)создает скрытый объект, IntStreamа операция карты IntStreamтребует IntUnaryOperator(т.е. функции int -> int). Функция сопоставления, которую вы хотите применить, не соблюдает этот контракт и, следовательно, ошибку компиляции.

Вам нужно будет позвонить boxed()раньше, чтобы получить Stream<Integer>(это то, что Arrays.asList(...).stream()возвращается). Затем просто позвоните, mapкак в первом фрагменте.

Обратите внимание: если вам нужно boxed(), то mapвы, вероятно, захотите использовать mapToObjнапрямую.

Преимущество в том, что mapToObjне требуется помещать каждое intзначение в Integerобъект; конечно, в зависимости от применяемой вами функции сопоставления; поэтому я бы выбрал этот вариант, который тоже короче.

Алексис С.
источник
5

Вы можете создать целочисленный поток, используя Arrays.stream (int []), вы можете вызвать mapToObjlike mapToObj(Integer::toString).

String csn = Arrays.stream(numbers) // your numbers array
.mapToObj(Integer::toString)
.collect(Collectors.joining(", "));

Надеюсь это поможет..

код-бот
источник
2

Никакого бокса, AFAIK, и никакого взрыва маленьких строк в кучу:

public static void main(String[] args) {
    IntStream stream = IntStream.of(1, 2, 3, 4, 5, 6);
    String s = stream.collect(StringBuilder::new, (builder, n) -> builder.append(',').append(n), (x, y) -> x.append(',').append(y)).substring(1);
    System.out.println(s);
}
Абу-Насар
источник
1

Если цель этого примера и вопроса - выяснить, как сопоставить строки с потоком целых чисел (например, используя поток целых чисел для доступа к индексу в массиве строк), вы также можете использовать бокс, а затем приведение к int (который затем позволит получить доступ к индексу массива).

int[] numbers = {0, 1, 2, 3}; 
String commaSeparatedNumbers = Arrays.stream(numbers)
    .boxed()
    .map((Integer i) -> Integer.toString((int)i))
    .collect(Collectors.joining(", "));

Вызов .boxed () преобразует ваш IntStream (поток примитивных целых чисел) в Stream (поток объектов, а именно, объекты Integer), который затем принимает возврат объекта (в данном случае объекта String) из ваша лямбда. Здесь это просто строковое представление числа для демонстрационных целей, но с такой же легкостью (и более практично) им может быть любой строковый объект - например, элемент массива строк, как упоминалось ранее.

Просто подумал, что предлагаю другую возможность. В программировании всегда есть несколько способов выполнить задачу. Узнайте как можно больше, а затем выберите тот, который лучше всего подходит для поставленной задачи, учитывая проблемы производительности, интуитивность, ясность кода, ваши предпочтения в стиле кодирования и наиболее самодокументированный.

Удачного кодирования!

jamesc1101
источник
1
Вы делаете ненужную работу. Вы упаковываете каждый intв свой тип оболочки Integerи сразу после этого распаковываете.
Alexis C.