Conversion de liste
Convertir une collection en une nouvelle collection est aussi simple que de la parcourir. Supposons que nous souhaitions convertir les noms d’une liste en majuscules. Jetons un coup d'œil à quelques méthodes de mise en œuvre.
Une chaîne en Java est immuable, elle ne peut donc pas être modifiée. Nous pouvons générer de nouvelles chaînes pour remplacer les éléments d'origine de la liste. Cependant, si vous faites cela, la liste originale disparaîtra ; il y a un autre problème. La liste originale peut également être immuable, comme celle générée par Arrays.asList(), donc la modification de la liste originale ne fonctionnera pas. Un autre inconvénient est qu’il est difficile de fonctionner en parallèle.
Générer une nouvelle liste en majuscules est un bon choix.
À première vue, cela peut sembler un conseil médiocre ; la performance est une question qui nous tient tous à cœur. Étonnamment, la programmation fonctionnelle est souvent plus performante que la programmation impérative, comme nous l'expliquons dans la section Problèmes de performances à la page 153.
Commençons par utiliser cet ensemble pour générer un nouvel ensemble de lettres majuscules.
Copiez le code comme suit :
final List<String> uppercaseNames = new ArrayList<String>();
pour(Nom de la chaîne : amis) {
uppercaseNames.add(name.toUpperCase());
}
Dans le code impératif, nous créons d'abord une liste vide, puis la remplissons de noms en majuscules, en les insérant un par un tout en parcourant la liste d'origine. Afin d'améliorer vers une version fonctionnelle, notre première étape peut être d'envisager de remplacer la boucle for par l'itérateur interne forEach mentionné lors du parcours de la liste à la page 19, comme le montre l'exemple suivant.
Copiez le code comme suit :
final List<String> uppercaseNames = new ArrayList<String>();
amis.forEach(nom -> uppercaseNames.add(name.toUpperCase()));
System.out.println(uppercaseNames);
Nous utilisons un itérateur interne, mais nous devons également créer une nouvelle liste et y insérer des éléments. Nous pouvons encore nous améliorer.
Utiliser des expressions lambda
Il existe une méthode map dans la nouvelle interface Stream, qui peut nous aider à éviter la variabilité et à rendre le code plus concis. Steam est un peu comme un itérateur de collection, mais il fournit également des fonctions fluides. En utilisant les méthodes de cette interface, nous pouvons combiner une série d'appels pour que le code se lit comme l'ordre décrivant le problème, le rendant plus lisible.
La méthode cartographique de Steam peut être utilisée pour convertir une séquence d'entrée en une séquence de sortie - ce qui correspond parfaitement à ce que nous voulons faire.
Copiez le code comme suit :
amis.stream()
.map(nom -> nom.toUpperCase())
.forEach(nom -> System.out.print(nom + " "));
System.out.println();
Toutes les collections du JDK8 prennent en charge cette méthode de flux, qui encapsule la collection dans une instance Steam. La méthode map appelle l'expression lambda ou le bloc de code spécifié pour chaque élément du Stream. La méthode map est très différente de la méthode forEach. forEach exécute simplement une fonction spécifiée sur les éléments de la collection. La méthode map collecte les résultats en cours d'exécution de l'expression lambda et renvoie un jeu de résultats. Enfin, nous imprimons tous les éléments en utilisant la méthode forEach.
Les noms de la nouvelle collection sont tous en majuscules :
Copiez le code comme suit :
BRIAN NATE NEAL RAJU SARA SCOTT
La méthode map est très adaptée pour transformer une collection d’entrée en une nouvelle collection de sortie. Cette méthode garantit que les séquences d’entrée et de sortie ont le même nombre d’éléments. Toutefois, les éléments d’entrée et de sortie peuvent être de types différents. Dans cet exemple, notre entrée et notre sortie sont toutes deux des collections de chaînes. On peut passer un morceau de code à la méthode map pour qu'elle renvoie, par exemple, le nombre de caractères contenus dans le nom. Dans ce cas, l’entrée est toujours une séquence de chaînes, mais la sortie est une séquence de nombres, comme suit.
Copiez le code comme suit :
amis.stream()
.map(nom -> nom.longueur())
.forEach(count -> System.out.print(count + " "));
Le résultat est le nombre de lettres dans chaque nom :
Copiez le code comme suit :
5 4 4 4 4 5
Une version ultérieure de l'expression lambda est utilisée pour éviter les opérations de modification explicites ; un tel code est très concis. Écrire de cette manière ne nécessite plus l'initialisation d'une collection vide et la variable garbage est masquée dans l'implémentation sous-jacente.
Référence de la méthode d'utilisation
Nous pouvons également utiliser des références de méthode pour le rendre un peu plus concis. Lorsqu'une implémentation d'une interface fonctionnelle doit être transmise, le compilateur Java peut accepter des expressions lambda ou des références de méthode. Avec cette fonctionnalité, vous pouvez remplacer name -> name.toUpperCase() par String::toUpperCase, comme ceci :
Copiez le code comme suit :
amis.stream()
.map(String::toUpperCase)
.forEach(nom -> System.out.println(nom));
Lorsque les paramètres sont passés dans la méthode générée - l'implémentation de la méthode abstraite de l'interface fonctionnelle - Java appellera la méthode toUpperCase du paramètre String. Cette référence de paramètre est masquée ici. Dans un scénario simple comme celui ci-dessus, nous pouvons utiliser des références de méthode pour remplacer les expressions lambda ; pour plus d'informations, voir quand les références de méthode doivent être utilisées à la page 26.
Copiez le code comme suit :
Un ami a demandé :
Quand devez-vous utiliser les références de méthode ?
Lors de la programmation en Java, nous utilisons généralement bien plus d’expressions lambda que de références de méthodes. Mais cela ne signifie pas que les références aux méthodes sont sans importance ou inutiles. Lorsque les expressions lambda sont très courtes, elles constituent une bonne alternative à l’appel direct de méthodes d’instance ou de méthodes statiques. En d’autres termes, si l’expression lambda ne transmet que des paramètres à l’appel de méthode, nous devrions plutôt utiliser des références de méthode.
Une expression lambda comme celle-ci ressemble un peu à ce que disait Tom Smykowski dans le film "Working with a Bug", son travail est de "transmettre les exigences des clients aux ingénieurs logiciels". Pour cette raison, j’appelle ce modèle de méthode de refactoring faisant référence au modèle de ver.
En plus d'être concise, l'utilisation de références de méthode peut mieux refléter la signification et la fonction du nom de méthode lui-même.
Derrière l’utilisation de références de méthodes, le compilateur joue un rôle clé. L'objet cible et les paramètres référencés par la méthode seront dérivés des paramètres transmis dans la méthode générée. Cela vous permet d'utiliser des références de méthodes pour écrire du code plus concis que d'utiliser des expressions lambda. Cependant, si les paramètres doivent être modifiés avant d'être transmis à la méthode ou si le résultat de l'appel est renvoyé, nous ne pouvons pas utiliser cette méthode d'écriture pratique.
Dans l'exemple précédent, la référence de méthode fait référence à une méthode d'instance. Les références de méthode peuvent également faire référence à une méthode statique et à une méthode qui accepte des paramètres. Nous en verrons des exemples plus tard.
Les expressions Lambda peuvent nous aider à parcourir les collections et à transformer les collections. Comme nous le verrons ci-dessous, cela nous aide également à sélectionner rapidement un élément dans une collection.