Saya baru saja menemukan pertanyaan saat menggunakan a List
dan stream()
metodenya. Meskipun saya tahu cara menggunakannya, saya tidak yakin kapan harus menggunakannya.
Misalnya, saya punya daftar, berisi berbagai jalur ke lokasi berbeda. Sekarang, saya ingin memeriksa apakah satu jalur tertentu berisi salah satu jalur yang ditentukan dalam daftar. Saya ingin mengembalikan boolean
berdasarkan apakah kondisi terpenuhi atau tidak.
Ini tentu saja, bukanlah tugas yang sulit. Tapi saya bertanya-tanya apakah saya harus menggunakan stream, atau loop for (-each).
Daftar
private static final List<String> EXCLUDE_PATHS = Arrays.asList(new String[]{
"my/path/one",
"my/path/two"
});
Contoh - Streaming
private boolean isExcluded(String path){
return EXCLUDE_PATHS.stream()
.map(String::toLowerCase)
.filter(path::contains)
.collect(Collectors.toList())
.size() > 0;
}
Contoh - Untuk-Setiap Loop
private boolean isExcluded(String path){
for (String excludePath : EXCLUDE_PATHS) {
if(path.contains(excludePath.toLowerCase())){
return true;
}
}
return false;
}
Perhatikan bahwa path
parameternya selalu huruf kecil .
Tebakan pertama saya adalah pendekatan untuk setiap lebih cepat, karena loop akan segera kembali, jika kondisinya terpenuhi. Sedangkan aliran masih akan mengulang semua entri daftar untuk menyelesaikan pemfilteran.
Apakah asumsi saya benar? Jika ya, mengapa (atau lebih tepatnya kapan ) saya akan menggunakannya stream()
?
sumber
new String[]{…}
disini. Cukup gunakanArrays.asList("my/path/one", "my/path/two")
String[]
, tidak perlu meneleponArrays.asList
. Anda bisa melakukan streaming melalui array menggunakanArrays.stream(array)
. Ngomong-ngomong, saya kesulitan memahami tujuanisExcluded
tes sama sekali. Sungguh menarik apakah elemen dariEXCLUDE_PATHS
benar-benar terkandung di suatu tempat di dalam jalur? YaituisExcluded("my/path/one/foo/bar/baz")
akan kembalitrue
, sertaisExcluded("foo/bar/baz/my/path/one/")
...Arrays.stream
metodenya, terima kasih telah menunjukkannya. Memang, contoh yang saya posting sepertinya tidak berguna bagi orang lain selain saya. Saya mengetahui perilakuisExcluded
metode ini, tetapi itu benar-benar hanya sesuatu yang saya butuhkan untuk diri saya sendiri, oleh karena itu, untuk menjawab pertanyaan Anda: ya , ini menarik karena alasan yang tidak ingin saya sebutkan, karena tidak sesuai dengan ruang lingkup dari pertanyaan awal.toLowerCase
diterapkan pada konstanta yang sudah berbentuk huruf kecil? Bukankah seharusnya itu diterapkan padapath
argumen?Jawaban:
Asumsi Anda benar. Implementasi streaming Anda lebih lambat daripada for-loop.
Penggunaan aliran ini harus secepat loop-for:
Ini mengulangi item, menerapkan
String::toLowerCase
dan menyaring item satu per satu dan mengakhiri item pertama yang cocok.Keduanya
collect()
&anyMatch()
adalah operasi terminal.anyMatch()
keluar di item pertama yang ditemukan, sementaracollect()
mengharuskan semua item untuk diproses.sumber
findFirst()
kombinasi denganfilter()
. Ternyata, saya tidak tahu bagaimana menggunakan aliran seperti yang saya pikirkan.Keputusan apakah akan menggunakan Stream atau tidak tidak harus didasarkan pada pertimbangan performa, melainkan oleh keterbacaan. Jika menyangkut kinerja, ada pertimbangan lain.
Dengan
.filter(path::contains).collect(Collectors.toList()).size() > 0
pendekatan Anda, Anda memproses semua elemen dan mengumpulkannya menjadi sementaraList
, sebelum membandingkan ukurannya, tetap saja, ini hampir tidak pernah menjadi masalah bagi Stream yang terdiri dari dua elemen.Penggunaan
.map(String::toLowerCase).anyMatch(path::contains)
dapat menghemat siklus CPU dan memori, jika Anda memiliki jumlah elemen yang jauh lebih besar. Namun, ini mengubah masingString
- masing menjadi representasi huruf kecil, sampai ditemukan kecocokan. Jelas, ada gunanya menggunakansebagai gantinya. Jadi, Anda tidak perlu mengulangi konversi ke huruf kecil di setiap permintaan
isExcluded
. Jika jumlah elemen dalamEXCLUDE_PATHS
atau panjang senar menjadi sangat besar, Anda dapat mempertimbangkan untuk menggunakanMengompilasi string sebagai pola regex dengan
LITERAL
bendera, membuatnya berperilaku seperti operasi string biasa, tetapi memungkinkan mesin menghabiskan beberapa waktu dalam persiapan, misalnya menggunakan algoritme Boyer Moore, agar lebih efisien dalam hal perbandingan aktual.Tentu saja, ini hanya terbayar jika ada cukup tes berikutnya untuk mengkompensasi waktu yang dihabiskan dalam persiapan. Menentukan apakah ini akan menjadi kasusnya, adalah salah satu pertimbangan kinerja yang sebenarnya, selain pertanyaan pertama apakah operasi ini akan pernah menjadi kinerja yang kritis sama sekali. Bukan pertanyaan apakah akan menggunakan Stream atau
for
loop.Ngomong-ngomong, contoh kode di atas tetap menggunakan logika kode asli Anda, yang terlihat meragukan bagi saya.
isExcluded
Metode Anda mengembalikantrue
, jika jalur yang ditentukan berisi salah satu elemen dalam daftar, sehingga mengembalikantrue
untuk/some/prefix/to/my/path/one
, sertamy/path/one/and/some/suffix
atau bahkan/some/prefix/to/my/path/one/and/some/suffix
.Even
dummy/path/onerous
dianggap memenuhi kriteria sebagaicontains
stringmy/path/one
…sumber
Ya. Kamu benar. Pendekatan aliran Anda akan memiliki beberapa overhead. Tetapi Anda dapat menggunakan konstruksi seperti itu:
Alasan utama menggunakan aliran adalah karena aliran membuat kode Anda lebih sederhana dan mudah dibaca.
sumber
anyMatch
jalan pintas untukfilter(...).findFirst().isPresent()
?Tujuan stream di Java adalah untuk menyederhanakan kerumitan penulisan kode paralel. Ini terinspirasi oleh pemrograman fungsional. Aliran serial hanya untuk membuat kode lebih bersih.
Jika kita menginginkan kinerja kita harus menggunakan parallelStream, yang telah dirancang untuk. Yang serial, secara umum, lebih lambat.
Ada artikel bagus untuk dibaca tentang , dan Kinerja .
ForLoop
Stream
ParallelStream
Dalam kode Anda, kami dapat menggunakan metode terminasi untuk menghentikan pencarian pada pertandingan pertama. (anyMatch ...)
sumber
Seperti orang lain telah menyebutkan banyak poin bagus, tetapi saya hanya ingin menyebutkan evaluasi malas dalam evaluasi aliran. Saat kami
map()
membuat aliran jalur huruf kecil, kami tidak langsung membuat seluruh aliran, melainkan aliran dibuat secara malas , itulah sebabnya kinerja harus setara dengan perulangan for tradisional. Itu tidak melakukan pemindaian penuh,map()
dananyMatch()
dijalankan pada saat yang bersamaan. SetelahanyMatch()
mengembalikan nilai true, itu akan dihubung pendek.sumber