Mengapa saya tidak bisa memetakan bilangan bulat ke string saat streaming dari array?

94

Kode ini berfungsi (diambil di Javadoc):

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

Yang ini tidak dapat dikompilasi:

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

IDEA memberi tahu saya bahwa saya memiliki "String jenis kembalian yang tidak kompatibel dalam ekspresi lambda".

Kenapa? Dan bagaimana cara memperbaikinya?

Denys Séguret
sumber

Jawaban:

121

Arrays.stream(int[])menciptakan IntStream, bukan Stream<Integer>. Jadi, Anda perlu menelepon, mapToObjbukan hanya map, saat memetakan intke suatu objek.

Ini harus bekerja seperti yang diharapkan:

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

yang juga bisa Anda tulis:

String commaSeparatedNumbers = Arrays.stream(numbers)
    .mapToObj(Integer::toString)
    .collect(Collectors.joining(", "));
assylias
sumber
3
Apa perbedaan antara IntStreamdan Stream<Integer>?
Florian Margaine
8
@FlorianMargaine An IntStreamadalah spesialisasi aliran untuk intnilai primitif . A Stream<Integer>hanyalah Arus yang menahan Integerbenda.
Alexis C.
2
@FlorianMargaine IntStreamadalah aliran atau primitif (ints) sedangkan Steram<Integer>aliran objek. Aliran primitif memiliki metode khusus untuk alasan kinerja.
assylias
7
IntStream.mapToObjmengharapkan sebuah IntFunction, fungsi yang mengkonsumsi suatu intnilai, oleh karena  .mapToObj((Integer i) -> i.toString())itu tidak berfungsi. Ini tidak akan direkomendasikan karena mengandung konversi yang tidak perlu dari intmenjadi Integer. Sebaliknya, .mapToObj(Integer::toString)berfungsi dengan baik karena akan memanggil staticmetode Integer.toString(int). Perhatikan bahwa ini berbeda dengan memanggil .map(Integer::toString)a  Stream<Integer>karena yang terakhir tidak dapat dikompilasi karena bersifat ambigu.
Holger
1
@cic: tidak, memanggil .map(Integer::toString)a Stream<Integer>benar-benar ambigu karena keduanya, .map(i->i.toString())dan .map(i->Integer.toString(i))valid. Tapi itu mudah dipecahkan dengan menggunakan .map(Object::toString).
Holger
19

Arrays.stream(numbers)membuat di IntStreambawah tenda dan operasi peta di IntStreammembutuhkan IntUnaryOperator(yaitu fungsi int -> int). Fungsi pemetaan yang ingin Anda terapkan tidak menghormati kontrak ini dan karenanya terjadi kesalahan kompilasi.

Anda harus menelepon boxed()sebelumnya untuk mendapatkan Stream<Integer>(inilah Arrays.asList(...).stream()hasil). Kemudian panggil saja mapseperti yang Anda lakukan di cuplikan pertama.

Perhatikan bahwa jika Anda perlu boxed()diikuti oleh mapAnda mungkin ingin menggunakan mapToObjsecara langsung.

Keuntungannya adalah mapToObjtidak perlu mengemas setiap intnilai ke suatu Integerobjek; tergantung pada fungsi pemetaan yang Anda terapkan tentunya; jadi saya akan menggunakan opsi ini yang juga lebih pendek untuk ditulis.

Alexis C.
sumber
5

Anda dapat membuat Stream Integer menggunakan Arrays.stream (int []), Anda dapat memanggil mapToObjsuka mapToObj(Integer::toString).

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

Semoga ini membantu..

codebot
sumber
2

Tidak ada tinju, AFAIK, dan tidak ada ledakan string kecil yang ditambahkan ke tumpukan:

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);
}
AbuNassar
sumber
1

Jika tujuan dari contoh dan pertanyaan ini adalah untuk mencari tahu bagaimana memetakan string ke aliran int (misalnya, menggunakan aliran int untuk mengakses indeks dalam Array string), Anda juga dapat menggunakan tinju, lalu mentransmisikan ke sebuah int (yang kemudian akan memungkinkan mengakses indeks dari array).

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

Panggilan .boxed () mengubah IntStream Anda (aliran int primitif) menjadi Stream (aliran objek - yaitu, objek Integer) yang kemudian akan menerima kembalinya objek (dalam hal ini, objek String) dari lambda Anda. Ini hanya representasi string dari angka untuk tujuan demonstrasi, tetapi bisa juga dengan mudah (dan lebih praktis) objek string apa pun - seperti elemen array string seperti yang disebutkan sebelumnya.

Hanya berpikir saya akan menawarkan kemungkinan lain. Dalam pemrograman, selalu ada banyak cara untuk menyelesaikan suatu tugas. Ketahui sebanyak mungkin yang Anda bisa, lalu pilih salah satu yang paling sesuai untuk tugas yang ada, dengan mempertimbangkan masalah kinerja, intuisi, kejelasan kode, preferensi Anda dalam gaya pengkodean, dan yang paling mendokumentasikan sendiri.

Selamat membuat kode!

jamesc1101
sumber
1
Anda melakukan pekerjaan yang tidak perlu. Anda mengotakkan masing int- masing ke jenis pembungkusnya Integerdan Anda membuka kotaknya setelahnya.
Alexis C.