Retour de lambda forEach () en java

97

J'essaie de changer certaines boucles for-each en forEach()méthodes lambda pour découvrir les possibilités des expressions lambda. Ce qui suit semble possible:

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

Avec lambda forEach()

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

Mais le suivant ne fonctionne pas:

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

avec lambda

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

Y a-t-il quelque chose qui ne va pas dans la syntaxe de la dernière ligne ou est-il impossible de revenir de la forEach()méthode?

Samutamm
la source
Je ne suis pas encore trop familier avec les internes des lambdas, mais quand je me pose la question: "De quoi reviendriez-vous?", Je soupçonne au départ que ce n'est pas la méthode.
Gimby
1
@Gimby Oui, returndans une instruction, lambda retourne du lambda lui-même, pas de ce qu'on appelle le lambda. La terminaison d'un flux précoce ("court-circuitage") utilise findFirstcomme indiqué dans la réponse de Ian Roberts .
Stuart marque

Réponses:

121

Il returny a un retour de l'expression lambda plutôt que de la méthode contenant. Au lieu de forEachvous avez besoin filterdu flux:

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

Ici filterrestreint le flux aux éléments qui correspondent au prédicat, findFirstpuis renvoie un Optionalavec la première entrée correspondante.

Cela semble moins efficace que l'approche de la boucle for, mais en fait findFirst()peut court-circuiter - il ne génère pas le flux filtré entier et n'en extrait pas un élément, mais filtre uniquement autant d'éléments qu'il le faut pour trouvez le premier correspondant. Vous pouvez également utiliser à la findAny()place de findFirst()si vous ne vous souciez pas nécessairement d'obtenir le premier joueur correspondant à partir du flux (ordonné), mais simplement n'importe quel élément correspondant. Cela permet une meilleure efficacité en cas de parallélisme.

Ian Roberts
la source
Merci, c'est ce que je cherchais! Il semble y avoir beaucoup de nouveautés à explorer dans Java8 :)
samutamm
10
Raisonnable, mais je vous suggère de ne pas utiliser orElse(null)sur un fichier Optional. Le point principal de Optionalest de fournir un moyen d'indiquer la présence ou l'absence d'une valeur au lieu de surcharger null (ce qui conduit à des NPE). Si vous l'utilisez, optional.orElse(null)il rachète tous les problèmes avec les valeurs nulles. Je ne l'utiliserais que si vous ne pouvez pas modifier l'appelant et qu'il attend vraiment un null.
Stuart marque
1
@StuartMarks en effet, modifier le type de retour de méthode en Optional<Player>serait un moyen plus naturel de s'intégrer dans le paradigme des flux. J'essayais juste de montrer comment dupliquer le comportement existant à l'aide de lambdas.
Ian Roberts
for (Part part: parts) if (! part.isEmpty ()) return false; Je me demande ce qui est vraiment plus court. Et plus clair. L'API de flux java a vraiment détruit le langage java et l'environnement java. Nightmare pour travailler dans n'importe quel projet java en 2020.
mmm
17

Je vous suggère d'essayer d'abord de comprendre Java 8 dans son ensemble, le plus important dans votre cas, ce sera les flux, les lambdas et les références de méthodes.

Vous ne devez jamais convertir du code existant en code Java 8 ligne par ligne, vous devez extraire les fonctionnalités et les convertir.

Ce que j'ai identifié dans votre premier cas est le suivant:

  • Vous souhaitez ajouter des éléments d'une structure d'entrée à une liste de sortie s'ils correspondent à un prédicat.

Voyons comment nous faisons cela, nous pouvons le faire avec ce qui suit:

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

Ce que vous faites ici est:

  1. Transformez votre structure d'entrée en flux (je suppose ici qu'elle est de type Collection<Player>, maintenant vous avez un fichier Stream<Player>.
  2. Filtrez tous les éléments indésirables avec un Predicate<Player>, mappant chaque joueur sur le booléen true s'il est souhaité qu'il soit conservé.
  3. Collectez les éléments résultants dans une liste, via un Collector, ici, nous pouvons utiliser l'un des collecteurs de bibliothèque standard, qui est Collectors.toList().

Cela intègre également deux autres points:

  1. Code contre interfaces, donc code contre List<E>over ArrayList<E>.
  2. Utiliser l'inférence de diamant pour le paramètre de type dans new ArrayList<>() , vous utilisez Java 8 après tout.

Passons maintenant à votre deuxième point:

Vous voulez à nouveau convertir quelque chose de Java hérité en Java 8 sans regarder la situation dans son ensemble. @IanRoberts a déjà répondu à cette partie , même si je pense que vous devez faire players.stream().filter(...)...ce qu'il a suggéré.

skiwi
la source
5

Si vous souhaitez renvoyer une valeur booléenne, vous pouvez utiliser quelque chose comme ceci (beaucoup plus rapide que le filtre):

players.stream().anyMatch(player -> player.getName().contains(name));
Sriram
la source
1

Vous pouvez également lever une exception:

Remarque:

Pour des raisons de lisibilité, chaque étape du flux doit être répertoriée dans une nouvelle ligne.

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

si votre logique est vaguement "pilotée par les exceptions", par exemple, il y a un endroit dans votre code qui intercepte toutes les exceptions et décide quoi faire ensuite. N'utilisez le développement axé sur les exceptions que lorsque vous pouvez éviter de joncher votre base de code avec des multiples try-catchet de lever ces exceptions pour des cas très particuliers dans lesquels vous les attendez et peuvent être gérés correctement.)

nabster
la source