Dalam banyak bahasa lain, mis. Haskell, sangat mudah untuk mengulang nilai atau fungsi beberapa kali, misalnya. untuk mendapatkan daftar 8 salinan dari nilai 1:
take 8 (repeat 1)
tapi saya belum menemukan ini di Java 8. Apakah ada fungsi seperti itu di JDK Java 8?
Atau sebagai alternatif sesuatu yang setara dengan rentang seperti
[1..8]
Tampaknya ini adalah pengganti yang jelas untuk pernyataan verbose di Java seperti
for (int i = 1; i <= 8; i++) {
System.out.println(i);
}
untuk memiliki sesuatu seperti
Range.from(1, 8).forEach(i -> System.out.println(i))
meskipun contoh khusus ini sebenarnya tidak terlihat lebih ringkas ... tapi semoga lebih mudah dibaca.
Jawaban:
Untuk contoh khusus ini, Anda dapat melakukan:
Jika Anda memerlukan langkah yang berbeda dari 1, Anda dapat menggunakan fungsi pemetaan, misalnya, untuk langkah 2:
Atau buat iterasi khusus dan batasi ukuran iterasi:
sumber
IntStream.rangeClosed(1, 8).forEach(i -> methodNoArgs());
) tetapi itu membingungkan IMO hal dan dalam hal ini loop tampaknya ditunjukkan.Inilah teknik lain yang saya lakukan beberapa hari yang lalu:
The
Collections.nCopies
panggilan menciptakanList
mengandungn
salinan dari nilai apa pun yang Anda berikan. Dalam hal ini nilai kotaknyaInteger
1. Tentu saja itu tidak benar-benar membuat daftar dengann
elemen; itu membuat daftar "tervirtualisasi" yang hanya berisi nilai dan panjang, dan setiap panggilan keget
dalam jangkauan hanya mengembalikan nilai. ThenCopies
Metode telah ada sejak Koleksi Kerangka diperkenalkan kembali dengan cara di JDK 1.2. Tentu saja, kemampuan untuk membuat aliran dari hasilnya ditambahkan di Java SE 8.Masalah besar, cara lain untuk melakukan hal yang sama di sekitar jumlah baris yang sama.
Namun, teknik ini lebih cepat dari
IntStream.generate
danIntStream.iterate
pendekatan, dan mengejutkan, itu juga lebih cepat dariIntStream.range
pendekatan.Untuk
iterate
dangenerate
hasilnya mungkin tidak terlalu mengejutkan. Kerangka aliran (sebenarnya, Pemisah untuk aliran ini) dibangun dengan asumsi bahwa lambda akan berpotensi menghasilkan nilai yang berbeda setiap saat, dan bahwa mereka akan menghasilkan jumlah hasil yang tidak terbatas. Hal ini membuat pemisahan paralel menjadi sulit. Theiterate
Metode juga bermasalah untuk kasus ini karena setiap panggilan membutuhkan hasil dari yang sebelumnya. Jadi aliran menggunakangenerate
daniterate
tidak melakukannya dengan baik untuk menghasilkan konstanta berulang.Kinerja yang relatif buruk
range
cukup mengejutkan. Ini juga tervirtualisasi, jadi elemen sebenarnya tidak semuanya ada di memori, dan ukurannya diketahui sebelumnya. Ini akan membuat spliterator yang cepat dan mudah diparalelkan. Tapi secara mengejutkan tidak berhasil dengan baik. Mungkin alasannya adalah karenarange
harus menghitung nilai untuk setiap elemen rentang dan kemudian memanggil fungsi di atasnya. Tapi fungsi ini mengabaikan inputnya dan mengembalikan konstanta, jadi saya terkejut ini tidak sebaris dan mati.The
Collections.nCopies
Teknik hubungannya tinju / pembukaan kemasan untuk menangani nilai-nilai, karena tidak ada spesialisasi primitifList
. Karena nilainya adalah sama setiap kali, pada dasarnya dikotak sekali dan kotak itu dibagikan oleh semuan
salinan. Saya menduga tinju / unboxing sangat dioptimalkan, bahkan diintrinsifikasi, dan dapat disisipkan dengan baik.Berikut kodenya:
Dan berikut adalah hasil JMH: (2.8GHz Core2Duo)
Ada cukup banyak variasi dalam versi ncopies, tetapi secara keseluruhan tampaknya nyaman 20x lebih cepat daripada versi range. (Namun, saya akan sangat bersedia untuk percaya bahwa saya telah melakukan sesuatu yang salah.)
Saya terkejut melihat seberapa baik
nCopies
teknik ini bekerja. Secara internal itu tidak terlalu istimewa, dengan aliran daftar virtual yang hanya diimplementasikan menggunakanIntStream.range
! Saya berharap akan perlu membuat spliterator khusus agar ini bekerja dengan cepat, tetapi tampaknya sudah cukup bagus.sumber
nCopies
tidak benar-benar menyalin apa pun dan "salinan" semuanya mengarah ke satu objek tunggal itu. Selalu aman jika objek itu tidak dapat diubah , seperti kotak primitif dalam contoh ini. Anda menyinggung hal ini dalam pernyataan "kotak sekali", tetapi mungkin bagus untuk secara eksplisit menyebutkan peringatan di sini karena perilaku tersebut tidak spesifik untuk auto-boxing.LongStream.range
secara signifikan lebih lambat dariIntStream.range
? Jadi adalah hal yang baik bahwa gagasan untuk tidak menawarkanIntStream
(tetapi digunakanLongStream
untuk semua tipe integer) telah dibatalkan. Perhatikan bahwa untuk kasus penggunaan berurutan, tidak ada alasan untuk menggunakan streaming sama sekali:Collections.nCopies(8, 1).forEach(i -> System.out.println(i));
melakukan hal yang samaCollections.nCopies(8, 1).stream().forEach(i -> System.out.println(i));
tetapi mungkin lebih efisienCollections.<Runnable>nCopies(8, () -> System.out.println(1)).forEach(Runnable::run);
LongStream.range
berkinerja lebih buruk, karena memiliki dua peta denganLongFunction
di dalamnya, sementarancopies
memiliki tiga peta denganIntFunction
,ToLongFunction
danLongFunction
, dengan demikian, semua lambda adalah monomorfik. Menjalankan pengujian ini pada profil tipe pra-polusi (yang lebih mendekati kasus dunia nyata) menunjukkan bahwancopies
1,5x lebih lambat.for
loop lama yang biasa . Meskipun solusi Anda lebih cepat daripadaStream
kode, tebakan saya adalah bahwafor
loop akan mengalahkan salah satu dari ini dengan margin yang signifikan.Untuk kelengkapan, dan juga karena saya tidak bisa menahan diri :)
Menghasilkan urutan konstanta terbatas cukup mirip dengan apa yang akan Anda lihat di Haskell, hanya dengan verboseness level Java.
sumber
() -> 1
hanya akan menghasilkan 1, apakah ini dimaksudkan? Jadi hasilnya akan seperti itu1 1 1 1 1 1 1 1
.take 8 (repeat 1)
. assylias cukup banyak menutupi semua kasus lainnya.Stream<T>
juga memilikigenerate
metode umum untuk mendapatkan aliran tak terbatas dari beberapa jenis lainnya, yang dapat dibatasi dengan cara yang sama.Setelah fungsi berulang di suatu tempat didefinisikan sebagai
Anda dapat menggunakannya sesekali dengan cara ini, misalnya:
Untuk mendapatkan dan setara dengan Haskell's
Anda bisa menulis
sumber
Runnable
menjadiFunction<Integer, ?>
dan kemudian menggunakanf.apply(i)
.Ini adalah solusi saya untuk mengimplementasikan fungsi waktu. Saya seorang junior jadi saya akui itu tidak ideal, saya akan senang mendengar jika ini bukan ide yang baik untuk alasan apa pun.
Berikut beberapa contoh penggunaan:
sumber