Kembali dari lambda forEach () di java


97

Saya mencoba mengubah beberapa loop untuk setiap menjadi forEach()-metode lambda untuk menemukan kemungkinan ekspresi lambda. Berikut ini tampaknya mungkin:

ArrayList<Player> playersOfTeam = new ArrayList<Player>();      
for (Player player : players) {
    if (player.getTeam().equals(teamName)) {
        playersOfTeam.add(player);
    }
}

Dengan lambda forEach()

players.forEach(player->{if (player.getTeam().equals(teamName)) {playersOfTeam.add(player);}});

Tapi yang berikutnya tidak berhasil:

for (Player player : players) {
    if (player.getName().contains(name)) {
        return player;
    }
}

dengan lambda

players.forEach(player->{if (player.getName().contains(name)) {return player;}});

Apakah ada yang salah dalam sintaks baris terakhir atau tidak mungkin untuk kembali dari forEach()metode?


Saya belum terlalu akrab dengan bagian dalam lambda, tetapi ketika saya mengajukan pertanyaan kepada diri saya sendiri: "Dari mana Anda akan kembali?", Kecurigaan awal saya adalah bahwa itu bukan metodenya.
Gimby

1
@ Gimby Ya, returndalam pernyataan lambda kembali dari lambda itu sendiri, bukan dari apa pun yang disebut lambda. Mengakhiri aliran awal ("arus pendek") digunakan findFirstseperti yang ditunjukkan dalam jawaban Ian Roberts .
Stuart Marks

Jawaban:


121

The returnada kembali dari ekspresi lambda bukan dari metode yang mengandung. Alih-alih forEachAnda perlu filterstreaming:

players.stream().filter(player -> player.getName().contains(name))
       .findFirst().orElse(null);

Di sini filtermembatasi aliran ke item-item yang cocok dengan predikat, dan findFirstkemudian mengembalikan Optionaldengan entri pertama yang cocok.

Ini terlihat kurang efisien daripada pendekatan for-loop, tetapi pada kenyataannya findFirst()dapat menyebabkan hubungan pendek - tidak menghasilkan seluruh aliran yang difilter dan kemudian mengekstrak satu elemen darinya, melainkan hanya menyaring elemen sebanyak yang diperlukan untuk temukan yang pertama cocok. Anda juga bisa menggunakan findAny()bukan findFirst()jika Anda tidak perlu peduli tentang mendapatkan pertama pemain yang cocok dari (memerintahkan) aliran tetapi hanya setiap item yang cocok. Ini memungkinkan efisiensi yang lebih baik ketika ada paralelisme yang terlibat.


Terima kasih, itulah yang saya cari! Sepertinya ada banyak hal baru di Java8 untuk dijelajahi :)
samutamm

10
Masuk akal, tetapi saya menyarankan agar Anda tidak menggunakan orElse(null)file Optional. Poin utama dari Optionaladalah menyediakan cara untuk menunjukkan ada atau tidaknya suatu nilai alih-alih membebani null (yang mengarah ke NPE). Jika Anda menggunakannya optional.orElse(null)membeli kembali semua masalah dengan nulls. Saya akan menggunakannya hanya jika Anda tidak dapat mengubah pemanggil dan itu benar-benar mengharapkan null.
Stuart Marks

1
@StuartMarks memang, mengubah metode tipe kembalian Optional<Player>menjadi cara yang lebih alami untuk menyesuaikan dengan paradigma aliran. Saya hanya mencoba menunjukkan bagaimana menduplikasi perilaku yang ada menggunakan lambda.
Ian Roberts

untuk (Part part: parts) if (! part.isEmpty ()) return false; Entah apa yang sebenarnya lebih pendek. Dan lebih jelas. Java stream api telah benar-benar menghancurkan bahasa java dan lingkungan java. Nightmare to work in any java project in 2020.
mmm

17

Saya sarankan Anda untuk mencoba memahami Java 8 secara keseluruhan, yang paling penting dalam kasus Anda itu adalah aliran, lambda dan referensi metode.

Anda tidak boleh mengonversi kode yang ada ke kode Java 8 secara baris demi baris, Anda harus mengekstrak fitur dan mengonversinya.

Apa yang saya identifikasi dalam kasus pertama Anda adalah sebagai berikut:

  • Anda ingin menambahkan elemen struktur masukan ke daftar keluaran jika cocok dengan beberapa predikat.

Mari kita lihat bagaimana kita melakukannya, kita bisa melakukannya dengan yang berikut:

List<Player> playersOfTeam = players.stream()
    .filter(player -> player.getTeam().equals(teamName))
    .collect(Collectors.toList());

Apa yang Anda lakukan di sini adalah:

  1. Ubah struktur input Anda menjadi aliran (saya berasumsi di sini bahwa ini adalah tipe Collection<Player>, sekarang Anda memiliki file Stream<Player>.
  2. Filter semua elemen yang tidak diinginkan dengan a Predicate<Player>, memetakan setiap pemain ke boolean true jika ingin disimpan.
  3. Kumpulkan elemen yang dihasilkan dalam daftar, melalui a Collector, di sini kita dapat menggunakan salah satu kolektor perpustakaan standar, yaitu Collectors.toList().

Ini juga menggabungkan dua poin lainnya:

  1. Kode terhadap interface, sehingga kode terhadap List<E>lebih ArrayList<E>.
  2. Gunakan inferensi berlian untuk parameter tipe di new ArrayList<>(), Anda juga menggunakan Java 8.

Sekarang ke poin kedua Anda:

Anda sekali lagi ingin mengubah sesuatu dari Java lama ke Java 8 tanpa melihat gambaran yang lebih besar. Bagian ini telah dijawab oleh @IanRoberts , meskipun menurut saya Anda perlu melakukan players.stream().filter(...)...apa yang dia sarankan.


5

Jika Anda ingin mengembalikan nilai boolean, Anda dapat menggunakan sesuatu seperti ini (jauh lebih cepat daripada filter):

players.stream().anyMatch(player -> player.getName().contains(name));


1

Anda juga dapat membuat pengecualian:

catatan:

Demi keterbacaan, setiap langkah aliran harus dicantumkan di baris baru.

players.stream()
       .filter(player -> player.getName().contains(name))
       .findFirst()
       .orElseThrow(MyCustomRuntimeException::new);

jika logika Anda secara longgar "didorong oleh pengecualian" seperti ada satu tempat di kode Anda yang menangkap semua pengecualian dan memutuskan apa yang harus dilakukan selanjutnya. Hanya gunakan pengembangan yang didorong pengecualian ketika Anda dapat menghindari mengotori basis kode Anda dengan kelipatan try-catchdan membuang pengecualian ini untuk kasus yang sangat khusus yang Anda harapkan dan dapat ditangani dengan benar.)

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.