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

Этот код работает (в 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 в выражении лямбда".

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

Ответ 1

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

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

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(", "));

Ответ 2

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

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

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

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

Ответ 3

Вы можете создать Integer Stream с помощью Arrays.stream(int []), вы можете вызвать mapToObj как mapToObj(Integer::toString).

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

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

Ответ 4

Если целью этого примера и вопроса является определение того, как сопоставить строки потоку данных int (например, используя поток int для доступа к индексу в массиве строк), вы также можете использовать бокс, затем кастинг в 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 (поток примитивных ints) в Stream (поток объектов, а именно объекты Integer), который затем примет возврат объекта (в данном случае String объект) из вашей лямбды. Здесь это просто строковое представление числа для демонстрационных целей, но так же легко (и более практически) быть любым строковым объектом - как элемент массива строк, как упоминалось ранее.

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

Счастливое кодирование!

Ответ 5

Нет бокса, 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);
}