Misalkan saya memiliki aliran Things dan saya ingin "memperkaya" mereka mid stream, saya dapat menggunakannya peek()
untuk melakukan ini, misalnya:
streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);
Asumsikan bahwa mutasi Hal pada titik ini dalam kode adalah perilaku yang benar - misalnya, thingMutator
metode ini dapat menetapkan bidang "lastProcessed" ke waktu saat ini.
Namun, peek()
dalam sebagian besar konteks berarti "lihat, tapi jangan sentuh".
Apakah menggunakan peek()
untuk mengubah elemen stream adalah antipattern atau keliru?
Edit:
Alternatifnya, yang lebih konvensional, adalah mengubah konsumen:
private void thingMutator(Thing thing) {
thing.setLastProcessed(System.currentTimeMillis());
}
ke fungsi yang mengembalikan parameter:
private Thing thingMutator(Thing thing) {
thing.setLastProcessed(currentTimeMillis());
return thing;
}
dan gunakan map()
sebagai gantinya:
stream.map(this::thingMutator)...
Tapi itu memperkenalkan kode ala kadarnya ( return
dan) dan saya tidak yakin itu lebih jelas, karena Anda tahu peek()
mengembalikan objek yang sama, tetapi dengan map()
itu bahkan tidak jelas sekilas bahwa itu adalah kelas objek yang sama.
Selanjutnya, dengan peek()
Anda dapat memiliki lambda yang bermutasi, tetapi dengan map()
Anda harus membangun bangkai kereta. Membandingkan:
stream.peek(t -> t.setLastProcessed(currentTimeMillis())).forEach(...)
stream.map(t -> {t.setLastProcessed(currentTimeMillis()); return t;}).forEach(...)
Saya pikir peek()
versinya lebih jelas, dan lambda jelas bermutasi, jadi tidak ada efek samping "misterius". Demikian pula, jika referensi metode digunakan dan nama metode jelas menyiratkan mutasi, itu juga jelas dan jelas.
Pada catatan pribadi, saya tidak menghindar dari penggunaan peek()
untuk bermutasi - Saya merasa sangat nyaman.
sumber
peek
aliran yang menghasilkan elemen-elemennya secara dinamis? Apakah masih berfungsi, atau perubahannya hilang? Mengubah elemen aliran terdengar tidak dapat diandalkan bagi saya.List<Thing> list; things.stream().peek(list::add).forEach(...);
sangat berguna. Belakangan ini. Aku sudah menggunakannya untuk menambahkan info untuk penerbitan:Map<Thing, Long> timestamps = ...; return things.stream().peek(t -> t.setTimestamp(timestamp.get(t))).collect(toList());
. Saya tahu ada cara lain untuk melakukan contoh ini, tetapi saya sangat menyederhanakan di sini. Menggunakanpeek()
menghasilkan IMHO kode yang lebih kompak, dan lebih elegan. Selain keterbacaan, pertanyaan ini sebenarnya tentang apa yang Anda ajukan; apakah aman / dapat diandalkan?peek
? Saya punya pertanyaan serupa tentang stackoverflow dan berharap Anda bisa melihatnya dan memberikan tanggapan Anda. Terima kasih. stackoverflow.com/questions/47356992/…Jawaban:
Anda benar, "mengintip" dalam arti bahasa Inggris dari kata itu berarti "lihat, tapi jangan sentuh."
Namun yang javadoc negara:
Kata kunci: "melakukan ... aksi" dan "dikonsumsi". JavaDoc sangat jelas bahwa kita diharapkan
peek
memiliki kemampuan untuk mengubah aliran.Namun JavaDoc juga menyatakan:
Ini menunjukkan bahwa ini lebih ditujukan untuk mengamati, misalnya mencatat elemen dalam aliran.
Apa yang saya ambil dari semua ini adalah bahwa kita dapat melakukan tindakan menggunakan elemen-elemen dalam stream, tetapi harus menghindari mutasi elemen dalam stream. Misalnya, lanjutkan dan panggil metode pada objek, tetapi cobalah untuk menghindari operasi bermutasi pada objek.
Paling tidak, saya akan menambahkan komentar singkat ke kode Anda di sepanjang baris ini:
Pendapat berbeda tentang kegunaan komentar tersebut, tetapi saya akan menggunakan komentar seperti itu dalam kasus ini.
sumber
thingMutator
, atau lebih konkretresetLastProcessed
dll. Kecuali ada alasan kuat, komentar kebutuhan seperti saran Anda biasanya menunjukkan pilihan variabel yang buruk dan / atau nama metode. Jika nama baik dipilih, bukankah itu cukup? Atau apakah Anda mengatakan bahwa meskipun memiliki nama baik, semua yang ada dipeek()
dalamnya seperti titik buta yang oleh sebagian besar programmer akan "meluncur" (secara visual dilewati)? Juga, "terutama untuk debugging" bukan hal yang sama dengan "hanya untuk debugging" - penggunaan apa selain yang "terutama" dimaksudkan?Itu bisa dengan mudah disalahartikan, jadi saya akan menghindari menggunakannya seperti ini. Mungkin opsi terbaik adalah menggunakan fungsi lambda untuk menggabungkan dua tindakan yang Anda perlukan ke dalam panggilan forEach. Anda mungkin juga ingin mempertimbangkan untuk mengembalikan objek baru daripada bermutasi yang sudah ada - mungkin sedikit kurang efisien, tetapi kemungkinan menghasilkan kode yang lebih mudah dibaca, dan mengurangi potensi secara tidak sengaja menggunakan daftar yang dimodifikasi untuk operasi lain yang seharusnya telah menerima yang asli.
sumber
Catatan API memberitahu kita bahwa metode ini telah ditambahkan terutama untuk tindakan seperti debugging / logging / firing stats dll.
@apiNote Metode ini ada terutama untuk mendukung debugging, di mana Anda ingin * melihat elemen ketika mereka melewati titik tertentu dalam pipa: *
sumber