Чому я не можу зіставити цілі числа з рядками під час потокової передачі з масиву?


92

Цей код працює (взято в 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 каже мені, що у мене є "несумісний рядок типу повернення у лямбда-виразі".

Чому? І як це виправити?

Відповіді:


114

Arrays.stream(int[])створює IntStream, а не a Stream<Integer>. Отже, вам потрібно зателефонувати mapToObjзамість просто map, коли зіставляєте an 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(", "));

3
Яка різниця між IntStreamта Stream<Integer>?
Флоріан Маргайн 13.03.15

8
@FlorianMargaine An IntStream- це спеціалізація потоку для примітивних intзначень. A Stream<Integer>- це просто потік, що утримує Integerоб'єкти.
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)а,  Stream<Integer>оскільки останній не компілюється, оскільки це неоднозначно.
Holger

1
@cic: ні, заклик .map(Integer::toString)до a Stream<Integer>справді неоднозначний, оскільки є обов’язковим .map(i->i.toString())і .map(i->Integer.toString(i))дійсним. Але це легко вирішити за допомогою .map(Object::toString).
Holger

19

Arrays.stream(numbers)створює IntStreamнижню частину капота, а операція картографії для an IntStreamвимагає IntUnaryOperator(тобто функції int -> int). Функція відображення, яку ви хочете застосувати, не поважає цей контракт і, отже, помилку компіляції.

Вам потрібно зателефонувати boxed()раніше, щоб отримати Stream<Integer>(це те, що Arrays.asList(...).stream()повертається). Тоді просто зателефонуйте, mapяк і у першому фрагменті.

Зверніть увагу, що якщо вам потрібно, boxed()а потім слід, mapви, ймовірно, хочете використовувати mapToObjбезпосередньо.

Перевага полягає в тому, mapToObjщо не потрібно поміщати кожне intзначення в Integerоб’єкт; залежно від функції відображення, яку ви застосовуєте, звичайно; тому я б вибрав цей варіант, який також коротший для написання.


5

Ви можете створити цілочисельний потік, використовуючи Arrays.stream (int []), ви можете зателефонувати mapToObjяк 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

Якщо метою цього зразка та питання є з'ясувати, як зіставити рядки з потоком ints (наприклад, використовувати потік ints для доступу до індексу в масиві рядків), ви також можете використовувати бокс, а потім передати в 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) у потік (потік об’єктів - а саме цілі об’єкти), який потім прийме повернення об’єкта (в даному випадку об’єкта String) з ваша лямбда. Тут це просто рядкове представлення числа для демонстраційних цілей, але це може так само легко (і більш практично) бути будь-яким рядковим об’єктом - як елемент рядкового масиву, як згадано раніше.

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

Щасливого кодування!


1
Ви робите непотрібну роботу. Ви встановлюєте поле кожного intтипу обгортки, Integerа потім розпаковуєте.
Alexis C.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.