Javada lambda forEach () 'den dönüş


103

forEach()Lambda ifadelerinin olasılıklarını keşfetmek için her bir döngü için bazılarını lambda yöntemlerine dönüştürmeye çalışıyorum . Aşağıdakiler mümkün görünüyor:

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

Lambda ile forEach()

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

Ama bir sonraki işe yaramıyor:

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

lambda ile

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

Son satırın söz diziminde bir sorun mu var yoksa forEach()yöntemden dönmek imkansız mı?


Henüz lambdaların iç kısımlarına pek aşina değilim, ama kendime şu soruyu sorduğumda: "Neden döneceksin?", İlk şüphem bunun yöntem olmadığıdır.
Gimby

1
@Gimby Evet, returnbir ifadede lambda, lambda adı verilen şeyden değil , lambda'nın kendisinden döner. Ian Roberts'ın cevabındafindFirst gösterildiği gibi bir akışı erken sonlandırın ("kısa devre yapan") kullanın .
Stuart Marks

Yanıtlar:


126

returnLambda ifade ziyade ihtiva eden bir yöntem ile ilgili bulunmaktadır dönüyor. Bunun yerine akışa forEachihtiyacınız var filter:

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

Burada filter, akışı yüklemle eşleşen öğelerle sınırlar ve findFirstardından Optionalilk eşleşen girdiye sahip bir döndürür .

Bu, for-loop yaklaşımından daha az verimli görünüyor, ancak gerçekte findFirst()kısa devre yapabilir - filtrelenmiş akışın tamamını oluşturmaz ve sonra ondan bir öğeyi çıkarmaz, bunun yerine yalnızca ihtiyaç duyduğu kadar çok öğeyi filtreler. ilk eşleşen olanı bulun. Bunun findAny()yerine , (sıralı) akıştan ilk eşleşen oyuncuyu findFirst()almayı umursamıyorsanız, ancak herhangi bir eşleşen öğeyi de kullanabilirsiniz. Bu, paralellik söz konusu olduğunda daha iyi verimlilik sağlar.


Teşekkürler, aradığım buydu! Java8'de keşfedilecek çok yeni şey var gibi görünüyor :)
samutamm

11
Makul, ama orElse(null)bir üzerinde kullanmamanızı öneririm Optional. Ana nokta, Optionalboşa aşırı yükleme yapmak yerine bir değerin varlığını veya yokluğunu göstermenin bir yolunu sağlamaktır (bu NPE'lere yol açar). Eğer kullanırsanız optional.orElse(null), tüm sorunları null ile geri alır. Sadece arayanı değiştiremezseniz ve gerçekten boş bir değer bekliyorsa kullanırım.
Stuart Marks

1
@StuartMarks gerçekten, yöntemin dönüş türünü değiştirmek Optional<Player>, akış paradigmasına uymanın daha doğal bir yolu olacaktır. Lambdas kullanarak mevcut davranışı nasıl kopyalayacağımı göstermeye çalışıyordum.
Ian Roberts

for (Part part: parts) if (! part.isEmpty ()) false döndürür; Gerçekten neyin daha kısa olduğunu merak ediyorum. Ve daha net. Java akışı api, java dilini ve java ortamını gerçekten yok etti. 2020'de herhangi bir java projesinde çalışma kabusu.
mmm

18

Öncelikle tüm resimde Java 8'i anlamaya çalışmanızı öneririm, en önemlisi sizin durumunuzda akımlar, lambdalar ve yöntem referansları olacaktır.

Sen gerektiğini asla bir çizgi-by-line olarak Java 8 koduna mevcut kodu dönüştürmek, sen özelliklerini ayıklamak ve bu dönüştürmek gerekir.

İlk vakanızda belirlediğim şey şudur:

  • Bazı yüklemlerle eşleşiyorlarsa, bir girdi yapısının öğelerini bir çıktı listesine eklemek istiyorsunuz.

Bakalım bunu nasıl yapıyoruz, bunu şu şekilde yapabiliriz:

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

Burada yaptığınız şey:

  1. Giriş yapınızı bir akışa dönüştürün (Burada bir tür olduğunu varsayıyorum Collection<Player>, şimdi bir Stream<Player>.
  2. Tüm istenmeyen öğeleri a ile filtreleyin Predicate<Player>, eğer saklanmak isteniyorsa her oyuncuyu gerçek boole ile eşleyin.
  3. Elde edilen öğeleri bir listede toplayın Collector, burada standart kütüphane toplayıcılarından birini kullanabiliriz, yani Collectors.toList().

Bu aynı zamanda iki noktayı daha içerir:

  1. Arayüzleri karşı Kod karşı koduna böylece List<E>üzerinde ArrayList<E>.
  2. Tür parametresi için elmas çıkarımı kullanın new ArrayList<>(), sonuçta Java 8 kullanıyorsunuz.

Şimdi ikinci noktaya gelelim:

Büyük resme bakmadan yine eski Java'dan bir şeyi Java 8'e dönüştürmek istiyorsunuz. Bu bölüm zaten @ IanRoberts tarafından yanıtlandı , ancak bence players.stream().filter(...)...onun önerdiklerini yapmanız gerektiğini düşünüyorum .


6

Bir boole değeri döndürmek istiyorsanız, bunun gibi bir şey kullanabilirsiniz (filtreden çok daha hızlı):

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


1

Ayrıca bir istisna da atabilirsiniz:

Not:

Okunabilirlik açısından akışın her adımı yeni satırda listelenmelidir.

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

mantığınız gevşek bir şekilde "istisnaya dayalı" ise, kodunuzda tüm istisnaları yakalayan ve daha sonra ne yapılacağına karar veren tek bir yer vardır. Yalnızca kod tabanınızı katlar ile kirletmekten kaçınabildiğiniz try-catchve bu istisnaları attığınız durumlarda istisna odaklı geliştirmeyi kullanın ve bu istisnaları beklediğiniz çok özel durumlar içindir ve uygun şekilde ele alınabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.