Sepertinya saya mengalami kesulitan memahami bagaimana Jawa menyusun operasi aliran ke dalam aliran pipa.
Saat menjalankan kode berikut
public
static void main(String[] args) {
StringBuilder sb = new StringBuilder();
var count = Stream.of(new String[]{"1", "2", "3", "4"})
.map(sb::append)
.count();
System.out.println(count);
System.out.println(sb.toString());
}
Konsol hanya mencetak 4
. The StringBuilder
objek masih memiliki nilai ""
.
Ketika saya menambahkan operasi filter: filter(s -> true)
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
var count = Stream.of(new String[]{"1", "2", "3", "4"})
.filter(s -> true)
.map(sb::append)
.count();
System.out.println(count);
System.out.println(sb.toString());
}
Output berubah menjadi:
4
1234
Bagaimana operasi filter yang tampaknya berlebihan ini mengubah perilaku pipa aliran yang dibuat?
java
java-stream
atalantus
sumber
sumber
Jawaban:
The
count()
operasi terminal, dalam versi saya dari JDK, akhirnya mengeksekusi kode berikut:Jika ada
filter()
operasi dalam pipa operasi, ukuran aliran, yang diketahui pada awalnya, tidak dapat diketahui lagi (karenafilter
bisa menolak beberapa elemen aliran). Jadiif
blok tidak dieksekusi, operasi menengah dieksekusi dan StringBuilder diubah.Di sisi lain, Jika Anda hanya memiliki
map()
dalam pipa, jumlah elemen dalam aliran dijamin sama dengan jumlah elemen awal. Jadi jika blok dijalankan, dan ukuran dikembalikan secara langsung tanpa mengevaluasi operasi perantara.Perhatikan bahwa lambda
map()
dianggap melanggar kontrak yang didefinisikan dalam dokumentasi: itu seharusnya merupakan operasi tanpa kewarganegaraan yang tanpa campur tangan, tetapi itu bukan stateless. Jadi memiliki hasil yang berbeda dalam kedua kasus tidak dapat dianggap sebagai bug.sumber
flatMap()
mungkin bisa mengubah jumlah elemen, apakah itu alasan mengapa awalnya bersemangat (sekarang malas)? Jadi, alternatifnya adalah menggunakanforEach()
dan menghitung secara terpisah jikamap()
dalam bentuk saat ini melanggar kontrak, saya kira.4 1234
tanpa menggunakan filter tambahan atau menghasilkan efek samping dalam operasi map ()?int count = array.length; String result = String.join("", array);
Collectors.joining("")
Dalam jdk-9 itu jelas didokumentasikan dalam java docs
Catatan API:
sumber
Ini bukan untuk apa .map. Seharusnya digunakan untuk mengubah aliran "Sesuatu" menjadi aliran "Sesuatu Lain". Dalam hal ini, Anda menggunakan peta untuk menambahkan string ke Stringbuilder eksternal, setelah itu Anda memiliki aliran "Stringbuilder", yang masing-masing dibuat oleh operasi peta menambahkan satu nomor ke Stringbuilder asli.
Aliran Anda tidak benar-benar melakukan apa pun dengan hasil yang dipetakan di arus, sehingga sangat masuk akal untuk menganggap bahwa langkah tersebut dapat dilewati oleh prosesor aliran. Anda mengandalkan efek samping untuk melakukan pekerjaan, yang merusak model fungsional peta. Anda akan lebih baik dilayani dengan menggunakan forEach untuk melakukan ini. Lakukan penghitungan sebagai aliran terpisah sepenuhnya, atau letakkan penghitung menggunakan AtomicInt di forEach.
Filter memaksanya untuk menjalankan konten stream karena sekarang harus melakukan sesuatu yang secara bermakna bermakna dengan setiap elemen stream.
sumber