Généricité, expressions lambda et flux en Java
DébutantUnité 11 sur 11 de la formation Formation : Java orienté objet : classes, collections, héritage et tests
Ce que tu vas apprendre
- Utilise des paramètres de type pour écrire des classes, des interfaces et des méthodes qui fonctionnent avec de nombreux types, tout en laissant le compilateur vérifier chaque utilisation.
- Définis et utilise des classes génériquesDéclare des classes et des interfaces avec des paramètres de type, crée des objets avec des arguments de type et explique la sécurité des types ainsi obtenue par rapport au stockage de valeurs de type
Object. - Écris des méthodes génériquesDéclare des méthodes avec leurs propres paramètres de type, appelle-les avec des arguments de type déduits ou explicites et détermine quand rendre une méthode générique plutôt qu’une classe.
- Restreins les paramètres de type avec des bornes et des types jokersUtilise des paramètres de type bornés pour appeler des méthodes sur une valeur de type générique, explique pourquoi une
List<Integer>n’est pas uneList<Number>et choisis entre? extendset? super. - Tiens compte des limites de l’effacement des typesExplique comment le compilateur efface les arguments de type, quelles opérations la généricité interdit par conséquent et pourquoi il faut éviter les types bruts.
- Définis et utilise des classes génériques
- Écris des expressions lambda et des références de méthodes pour des interfaces fonctionnelles, utilise les interfaces fonctionnelles standard de Java et transmets des comportements à des méthodes sous forme de valeurs.
- Écris des expressions lambda pour des interfaces fonctionnellesReconnais les interfaces fonctionnelles, écris des expressions lambda sous forme courte ou sous forme de bloc et explique comment le type cible donne son sens à une expression lambda.
- Utilise les interfaces fonctionnelles standardChoisis entre Predicate, Function, Consumer, Supplier et les interfaces apparentées, combine-les et transmets-les à des méthodes de collection telles que removeIf et replaceAll.
- Capture des variables et écris des références de méthodesUtilise dans une expression lambda des variables du code environnant, applique la règle des variables effectivement finales et remplace des expressions lambda simples par les quatre types de références de méthodes.
- Écris des expressions lambda pour des interfaces fonctionnelles
- Trie des listes selon la règle de ton choix à l’aide de comparateurs, d’une méthode compare écrite à la main à des chaînes construites avec comparing, thenComparing et reversed.
- Écris un comparateur et utilise-le pour trierImplémente correctement le contrat de compare, trie des listes et des tableaux avec un comparateur et choisis entre Comparable et Comparator.
- Combine des comparateurs avec comparing, thenComparing et reversedConstruis des comparateurs à partir de clés de tri, ajoute des critères de départage, inverse des ordres, gère les valeurs nulles et trie les entrées d’une Map.
- Écris un comparateur et utilise-le pour trier
- Construis des pipelines de l’API Stream qui filtrent, transforment, trient et réduisent des données, et explique comment un pipeline s’exécute.
- Construis un pipeline de flux et comprends son exécutionCrée des flux à partir de collections, de tableaux et de plages de valeurs, distingue les opérations intermédiaires des opérations terminales et explique l’évaluation paresseuse, l’usage unique et pourquoi la source reste inchangée.
- Filtre, transforme et réordonne des élémentsUtilise filter, map, mapToInt, flatMap, distinct, sorted, limit et skip, et passe des flux d’objets aux flux de types primitifs et inversement.
- Réduis un flux à un résultat uniqueCalcule des effectifs, des sommes, des moyennes, des extrema et des réponses vraies ou fausses avec des opérations terminales, et gère l’absence de résultat avec Optional.
- Construis un pipeline de flux et comprends son exécution
- Rassemble les résultats des flux dans des listes, des ensembles, des Map et des chaînes, regroupe et partitionne des données avec des collecteurs et détermine quand un flux ou une boucle est le meilleur outil.
- Collecte les résultats dans des listes, des ensembles, des Map et des chaînesUtilise toList, Collectors.toList, toSet, toCollection, joining et toMap, et gère les résultats non modifiables et les clés en double.
- Regroupe et partitionne des données avec des collecteursRépartis les données en groupes avec groupingBy et partitioningBy et résume chaque groupe avec un collecteur en aval.
- Choisis entre flux et boucles pour réaliser une tâche complèteRésous une tâche de traitement de données du début à la fin, compare une solution avec flux à une boucle et garde les pipelines sans effets de bord.
- Collecte les résultats dans des listes, des ensembles, des Map et des chaînes
Généricité et flux en Java : cours
1Écris des classes et des méthodes génériques
Le code générique préserve une relation utile entre les entrées, les valeurs stockées et les résultats. Une classe peut conserver cette relation pour un objet, tandis qu’une méthode peut la choisir à chaque appel. Les bornes et les types jokers permettent aux API d’accepter les types nécessaires sans autoriser d’écritures non sûres. Le compilateur vérifie ces engagements avant d’effacer les paramètres de type, ce qui explique à la fois les avantages de la généricité et les opérations qu’elle ne peut pas prendre en charge à l’exécution.
1.1 Définis et utilise des classes génériques
Un conteneur devrait indiquer ce qu’il contient. Avec un paramètre de type, une même classe peut contenir des chaînes, des nombres ou tes propres objets, et le compilateur vérifie chaque utilisation avant l’exécution du programme.
Donne un paramètre de type à une classe
Dans class Box<T>, T est un paramètre de type : un nom pour un type que le code appelant fournira. Utilise-le pour les champs, les paramètres de méthodes et les résultats.
class Box<T> {
private T value;
public Box(T value) { this.value = value; }
public T get() { return value; }
public void set(T value) { this.value = value; }
}
Le nom du constructeur est Box, sans <T>. La déclaration après le nom de la classe introduit T pour le corps de la classe. Crée maintenant une boîte :
Box<String> b = new Box<>("tea");
String drink = b.get();
b.set("water");
System.out.println(drink); // tea
System.out.println(b.get()); // water
String est l’argument de type. Pour cette variable, get() renvoie une String et set accepte une String. Aucun transtypage n’est nécessaire à la lecture. La tentative d’appel b.set(42) échoue à la compilation : « incompatible types: int cannot be converted to String ». L’erreur signale l’écriture incorrecte avant qu’une lecture ultérieure puisse échouer.
Pourquoi ne pas stocker un Object ?
Un champ Object peut référencer aussi bien une chaîne qu’un entier. Le code qui a besoin d’une chaîne doit effectuer un transtypage de l’objet renvoyé. Si un entier a été stocké, ce transtypage lève une ClassCastException à l’exécution du programme. Une Box<String> inscrit la relation voulue dans le type déclaré.
Le diamant <> dans new Box<>("tea") demande au compilateur de déduire les arguments de type. Il conserve les vérifications. Omettre complètement les chevrons, comme dans new Box("tea"), utilise un type brut et perd les vérifications liées au paramètre de type. Affecter une construction de type brut à une variable typée produit des avertissements de type non vérifié ; la variable typée continue à imposer son propre argument déclaré lors des appels suivants.
Utilise des classes enveloppes et plusieurs paramètres
Un argument de type doit être un type référence. Utilise Box<Integer>, pas Box<int>. Java convertit le littéral 5 en un Integer lors de la construction de new Box<Integer>(5) et le convertit à nouveau en int lorsqu’un calcul l’exige. Si n contient cette boîte, int next = n.get() + 1; donne 6.
Deux types indépendants nécessitent deux paramètres. Une Pair<K, V> peut avoir un champ K first et un champ V second. Pour une Pair<String, Integer>, ses accesseurs renvoient une String et un Integer. Les noms courants sont T pour un type, E pour un élément, K et V pour une clé et une valeur, et R pour un résultat.
Ajoute des paramètres de type aux interfaces
interface Container<T> {
void put(T item);
T take();
}
Une classe peut préserver le choix avec class Shelf<T> implements Container<T>, ou le fixer avec class NameList implements Container<String>. List<E> et Comparable<T> utilisent la même idée. Une classe générique reste une seule classe : Box<String> et Box<Integer> décrivent différentes utilisations vérifiées à la compilation, et non deux déclarations de classe. Ces types ne peuvent pas être affectés l’un à l’autre, car ils annoncent des types d’éléments différents.
Points clés
- Un paramètre de type nomme un type dans une déclaration ; un argument de type le fournit lors d’une utilisation.
- Un accesseur générique renvoie le type d’élément déclaré sans transtypage.
- Le diamant préserve les vérifications de type ; un type brut perd les vérifications génériques.
- Utilise des classes enveloppes telles que
Integercomme arguments de type. - Les classes et les interfaces peuvent avoir plusieurs paramètres de type.
1.2 Écris des méthodes génériques
Certaines opérations nécessitent de choisir un type pour un seul appel, et non pour toute la durée de vie d’un objet. Une méthode générique introduit son propre paramètre de type et préserve une relation entre son entrée et son résultat.
Déclare le type avant le type de retour
import java.util.List;
class Lists {
public static <T> T last(List<T> list) {
return list.get(list.size() - 1);
}
public static void main(String[] args) {
int number = last(List.of(4, 8, 15));
String word = Lists.<String>last(List.of("x", "y"));
System.out.println(number); // 15
System.out.println(word); // y
}
}
Lis la déclaration par parties. static en fait une méthode de classe. <T> introduit un paramètre de type appartenant à la méthode. Le T suivant est le type de retour. List<T> indique que les éléments de la liste d’entrée et le résultat ont le même type.
Pour le premier appel, Java déduit que T est Integer. Le résultat peut ensuite être converti automatiquement en int. Dans le second appel, <String> est un témoin de type explicite, placé entre le point et le nom de la méthode. Ici, Lists.last(...) permettrait de déduire le même type. Un témoin nécessite un élément avant le point : un nom de classe pour une méthode statique, ou un objet tel que this pour une méthode d’instance.
Cette méthode attend une liste non vide. Sur une liste vide, size() - 1 vaut -1, donc get lève une IndexOutOfBoundsException. La généricité vérifie les types ; elle ne prouve pas qu’un indice existe.
Relie plusieurs types
Une méthode générique peut introduire plusieurs paramètres. Avec une classe Pair<K, V> dotée d’un constructeur et d’accesseurs, cette méthode complète inverse les rôles :
static <K, V> Pair<V, K> swap(Pair<K, V> p) {
return new Pair<>(p.getSecond(), p.getFirst());
}
Pour la paire d’entrée ("Ada", 36) de type Pair<String, Integer>, le résultat est (36, "Ada") de type Pair<Integer, String>. La déclaration du type de retour exprime la relation ; un type de retour Object la masquerait et imposerait un transtypage au code appelant.
Une méthode telle que <T> int countEqual(List<T> list, T target) relie aussi ses entrées. Sa boucle peut comparer chaque élément à target avec equals. Avec ["a", "b", "a", "c", "a"] et "a", le nombre obtenu est 3. L’inférence peut choisir un supertype commun aux arguments ; répéter T n’impose donc pas toujours que deux expressions fournies en argument aient des types déclarés identiques.
Détermine à qui appartient le paramètre de type
Une Box<String> conserve son argument de type pour l’utilisation de cet objet. Lists.last choisit un nouvel argument de type à chaque appel. Une classe utilitaire ordinaire peut donc contenir des méthodes statiques génériques sans être elle-même générique.
Une méthode statique dans Box<T> ne peut pas utiliser le T de la classe : la méthode statique appartient à la classe partagée, et non à une boîte particulière. Déclare un paramètre distinct, par exemple static <E> Box<E> of(E value). Si une déclaration de méthode commence par static T first(...) sans <T> propre à la méthode, le compilateur indique qu’il ne trouve pas T dans une classe ordinaire. Ajoute la déclaration avant le type de retour ; remplacer le type de retour par Object ferait perdre la relation utile.
Points clés
- Une méthode générique déclare ses propres paramètres de type avant son type de retour.
- L’inférence est effectuée séparément pour chaque appel.
- Un témoin de type apparaît après un point et avant le nom de la méthode.
- Utilise plusieurs paramètres de type lorsque les entrées et les sorties ont plusieurs rôles liés.
- Une méthode statique doit déclarer son propre paramètre de type.
- La sécurité des types ne vérifie pas qu’une entrée respecte l’exigence de non-vacuité d’une méthode.
1.3 Restreins les paramètres de type avec des bornes et des types jokers
Un T sans restriction ne prend en charge que les opérations disponibles sur Object. Les bornes ajoutent une garantie utile. Les types jokers permettent à un paramètre d’accepter une famille de types génériques sans prétendre que tous leurs types d’éléments sont identiques.
Exige les opérations dont tu as besoin
static <T extends Comparable<T>> T largest(List<T> list) {
T best = list.get(0);
for (T x : list) {
if (x.compareTo(best) > 0) {
best = x;
}
}
return best;
}
La borne indique qu’un T peut se comparer à un autre T. En partant de 3, l’appel largest(List.of(3, 11, 7)) remplace la meilleure valeur par 11, puis la laisse inchangée pour 7. Il renvoie 11. Pour ["pear", "apple", "plum"], le dernier mot dans l’ordre naturel est "plum". Une liste vide provoque un échec à get(0).
extends introduit aussi les bornes pour les interfaces. <T extends Number> rend doubleValue() disponible. Les bornes multiples utilisent &, avec une classe en premier s’il y en a une : <T extends Number & Comparable<T>>.
Pourquoi List n’est pas List
Integer étend Number, mais List<Integer> n’est pas un sous-type de List<Number>. Les types génériques sont invariants. Si cette affectation était autorisée, le code utilisant la référence List<Number> pourrait ajouter un Double ; le code qui détient la liste d’origine le lirait alors comme un Integer. Un paramètre List<Number> accepte les implémentations telles que ArrayList<Number>, mais refuse List<Integer>.
Lis avec extends ; ajoute avec super
static double sum(List<? extends Number> nums) {
double total = 0;
for (Number n : nums) total += n.doubleValue();
return total;
}
static void addOneToThree(List<? super Integer> target) {
target.add(1);
target.add(2);
target.add(3);
}
? extends Number désigne un type inconnu qui est Number ou l’un de ses sous-types. sum accepte des listes d’entiers, de nombres à virgule flottante ou de nombres. La méthode lit chaque élément comme un Number. sum(List.of(1.5, 2.5)) donne 4,0. Elle ne peut pas ajouter 3 : le type d’élément inconnu pourrait être Double. Les règles de type autorisent l’ajout de null, même si une liste particulière peut le refuser à l’exécution.
? super Integer désigne Integer ou l’un de ses supertypes. La seconde méthode peut ajouter des entiers à une List<Integer>, une List<Number> ou une List<Object> modifiable. Une lecture par ce paramètre ne fournit que le type Object : les éléments existants pourraient être d’autres nombres ou objets.
| Paramètre | Opération sûre sur les éléments |
|---|---|
List<? extends T> | Lire un élément comme un T |
List<? super T> | Ajouter un T ; lire comme un Object |
List<T> | Lire et écrire avec le même type nommé |
List<?> | Lire comme un Object ; consulter la taille |
La règle mnémotechnique est producteur : extends, consommateur : super. Elle décrit le sens du transfert des éléments, et non l’immuabilité de la collection entière. Par exemple, il peut rester possible de supprimer des éléments par une vue utilisant extends.
Utilise un paramètre nommé lorsqu’un type doit relier plusieurs positions, notamment une entrée et un type de retour. Utilise un type joker lorsque seul un ensemble de types d’entrée acceptés compte. Collections.copy lit depuis une List<? extends T> et écrit dans une List<? super T> ; sa destination doit déjà avoir assez de positions à remplacer.
Points clés
- Les bornes rendent les méthodes du type qui sert de borne disponibles sur
T. - L’invariance des types génériques protège une liste contre l’écriture d’éléments de type incorrect.
- Utilise
? extends Tpour lire des éléments comme desT. - Utilise
? super Tpour ajouter des valeurs de typeT; les valeurs lues sont de typeObject. - Un type joker restreint les opérations effectuées par une référence ; il ne rend pas l’objet immuable.
- Utilise un paramètre de type nommé pour relier les types des entrées et du résultat.
Collections.copyremplace les éléments aux positions existantes de la destination.
1.4 Tiens compte des limites de l’effacement des types
La généricité donne au compilateur plus d’informations qu’un test ordinaire de classe à l’exécution. Comprendre ce qui reste après la compilation explique les avertissements liés aux types bruts et plusieurs opérations que Java refuse.
Ce que l’effacement supprime
Java vérifie les arguments de type, puis efface les paramètres de type en les remplaçant par leurs bornes. Un T non borné devient Object ; un T extends Number devient Number. Le compilateur insère des transtypages là où ils sont nécessaires à la lecture des valeurs. Il n’existe qu’une seule classe Box à l’exécution, donc ceci affiche true :
System.out.println(
new Box<String>("a").getClass()
== new Box<Integer>(1).getClass()
);
La déclaration de Box peut être celle de la classe générique dotée d’un champ de valeur, d’un constructeur et d’un accesseur. Les deux constructions ont la même classe à l’exécution, même si leurs utilisations déclarées font l’objet de vérifications différentes à la compilation.
Opérations qui nécessitent un type connu à l’exécution
Dans une classe générique, new T() ne compile pas. Java ne sait pas quel constructeur possède le type fourni par le code appelant. Accepte une fabrique pour créer la valeur :
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
class Factory {
static <T> T create(Supplier<T> factory) {
return factory.get();
}
public static void main(String[] args) {
List<String> words = create(ArrayList::new);
words.add("ready");
System.out.println(words); // [ready]
}
}
Le fournisseur définit le comportement de création. new T[10] et new List<Integer>[2] sont également refusés comme créations de tableaux génériques. Stocke une séquence dans une ArrayList<T> lorsque tu n’as pas besoin du comportement d’un tableau.
Pour un Object obj, obj instanceof List<String> ne peut pas tester de façon sûre l’argument de type des éléments. Utilise obj instanceof List<?> pour vérifier s’il s’agit d’une liste, puis traite ses éléments comme étant de type inconnu. Ce test n’établit pas que les éléments sont des chaînes.
Membres statiques partagés et surcharges après effacement
Un champ tel que static T shared ne peut pas utiliser le T de la classe englobante. Toutes les paramétrisations partagent le membre statique. Une méthode statique peut introduire son propre <E>, indépendant du paramètre de la classe.
Deux surcharges print(Set<String> s) et print(Set<Integer> s) ne peuvent pas coexister dans une même classe. Toutes deux deviennent après effacement une méthode acceptant Set, ce qui provoque un conflit de noms. Donne des noms différents aux opérations ou conçois une seule méthode qui gère leur comportement commun.
Remonte d’un avertissement de type non vérifié jusqu’à la lecture incorrecte
List raw = new ArrayList();
raw.add("text");
raw.add(42);
for (Object value : raw) {
String text = (String) value;
System.out.println(text);
}
Ce code affiche text, puis le transtypage de 42 lève une ClassCastException. La déclaration brute peut déclencher un diagnostic de type brut ; les ajouts déclenchent des avertissements d’appel non vérifié, car les vérifications génériques ont été contournées. La remplacer par List<String> déplace l’échec à add(42), lors de la compilation. Un avertissement de type non vérifié signale une garantie perdue, donc un risque d’échec à l’exécution. Corrige l’utilisation du type brut ou le transtypage non sûr au lieu de supposer qu’une lecture ultérieure sera sûre.
Points clés
- L’effacement remplace les paramètres de type par leurs bornes, ou par
Objectlorsqu’ils ne sont pas bornés. - Les utilisations paramétrées partagent une seule classe à l’exécution.
- Utilise une fabrique fournie en argument à la place de
new T(). - Les types des paramètres après effacement ne peuvent pas distinguer des surcharges génériques.
2Transmets des comportements avec des expressions lambda
Une méthode peut accepter un comportement aussi bien que des données. Les interfaces fonctionnelles décrivent la forme de ce comportement et les expressions lambda fournissent le travail à effectuer. Les interfaces standard donnent des noms clairs aux tâches courantes : tester, transformer, agir et fournir. Les variables capturées permettent à une règle d’utiliser des informations environnantes, tandis que les références de méthodes désignent des comportements existants. Le respect du type cible relie chaque élément de syntaxe à un appel de méthode ordinaire.
2.1 Écris des expressions lambda pour des interfaces fonctionnelles
Une méthode peut accepter une règle comme argument. Une expression lambda écrit cette règle là où elle est utilisée, tandis qu’une interface fonctionnelle indique à Java quelles entrées la règle prend et ce qu’elle renvoie.
Réponds à une seule méthode abstraite
Une interface fonctionnelle possède une méthode abstraite à implémenter. Les déclarations correspondant à des méthodes d’instance publiques de Object, comme equals(Object), ne comptent pas. Les méthodes par défaut et les méthodes statiques n’ajoutent pas de méthode abstraite à implémenter. Comparator<T> et Runnable sont des interfaces fonctionnelles.
@FunctionalInterface
interface Operation {
int apply(int a, int b);
}
class Combine {
static int combine(int a, int b, Operation op) {
return op.apply(a, b);
}
public static void main(String[] args) {
Operation add = (a, b) -> a + b;
System.out.println(add.apply(3, 4)); // 7
System.out.println(combine(3, 4, (x, y) -> x * y)); // 12
}
}
L’interface fournit le nom de méthode apply, les deux types de paramètres int et le type de résultat int. L’expression lambda fournit les noms des paramètres et le corps. Elle crée une implémentation utilisable par l’intermédiaire de Operation ; elle n’appelle pas apply tant que le programme ne l’invoque pas.
combine transmet toujours ses deux nombres à op. Le code appelant change le comportement en fournissant une addition ou une multiplication. C’est aussi de cette façon que sort reçoit une règle d’ordre et que filter reçoit une condition.
Lis la syntaxe
Une expression lambda comporte des paramètres, ->, puis une expression ou un bloc. Un seul paramètre dont le type est déduit peut s’écrire sans parenthèses : w -> w.length() > 5. En l’absence de paramètre, il faut écrire () ; deux paramètres ou plus nécessitent des parenthèses, comme dans (a, b) -> a * b. Les types explicites sont autorisés : (int a, int b) -> a + b.
Un corps sous forme d’expression renvoie automatiquement son résultat. Un bloc qui produit une valeur doit la renvoyer :
Operation max = (a, b) -> {
if (a > b) {
return a;
}
return b;
};
System.out.println(max.apply(3, 9)); // 9
Chaque chemin dans ce bloc renvoie un int. Ajouter des accolades autour de a + b sans return ne crée pas un bloc valide qui renvoie une valeur.
Laisse le type cible donner son sens à l’expression
Le type cible est l’interface fonctionnelle attendue dans une affectation, un argument ou un retour. Le texte x -> x * 2 peut convenir à Function<Integer, Integer> ou à UnaryOperator<Integer>. Les types de son paramètre et de son résultat proviennent de ce type cible. Object o = () -> 1; échoue parce que Object ne fournit aucune méthode fonctionnelle.
@FunctionalInterface demande au compilateur de vérifier la définition de l’interface. C’est une protection utile si quelqu’un ajoute plus tard une seconde méthode abstraite distincte. Sans l’annotation, une telle interface peut encore être déclarée, mais une expression lambda ne peut pas répondre à ses deux exigences distinctes.
Une expression lambda peut aussi entrer en conflit avec des noms locaux. Si la méthode environnante déclare déjà int a, elle ne peut pas déclarer un paramètre d’expression lambda nommé a dans cette portée. Choisis un autre nom de paramètre. Par rapport à une classe anonyme qui implémente Comparator<String>, une expression lambda supprime la répétition des déclarations de classe et de méthode ; le calcul qui détermine l’ordre doit toujours respecter le contrat du comparateur.
Points clés
- Une expression lambda fournit l’implémentation de la méthode abstraite d’une interface fonctionnelle.
- L’interface cible détermine les types d’entrée, le type de résultat et le nom de la méthode.
- Un corps sous forme d’expression renvoie directement une valeur ; un bloc qui renvoie une valeur nécessite
return. @FunctionalInterfacevérifie la définition de l’interface.- Un paramètre d’expression lambda ne peut pas redéclarer une variable locale de la portée environnante.
2.2 Utilise les interfaces fonctionnelles standard
Java nomme déjà les formes courantes de comportement. Choisis une interface selon le nombre d’entrées et le type de résultat, puis appelle sa méthode fonctionnelle pour exécuter le comportement.
Choisis la forme des entrées et de la sortie
| Interface | Méthode fonctionnelle | Rôle |
|---|---|---|
Predicate<T> | boolean test(T t) | Tester une condition |
Function<T, R> | R apply(T t) | Transformer une valeur |
Consumer<T> | void accept(T t) | Effectuer une action |
Supplier<T> | T get() | Fournir une valeur sans entrée |
Ces interfaces se trouvent dans java.util.function. Pour des chaînes, w -> w.length() > 5 convient à un prédicat, String::length à une fonction qui renvoie un Integer, et System.out::println à un consommateur. Un fournisseur tel que () -> new ArrayList<String>() crée une nouvelle liste à chaque exécution de get(). Une condition utilisée par filter nécessite un Predicate<T>, même si une Function<T, Boolean> pourrait aussi calculer une valeur booléenne dans un autre code.
UnaryOperator<T> est une fonction qui transforme un T en un autre T de même type. BinaryOperator<T> combine deux valeurs de type T en un T. BiFunction<T, U, R>, BiPredicate<T, U> et BiConsumer<T, U> prennent deux entrées. Les formes pour types primitifs évitent la conversion en objets enveloppes : IntPredicate.test(int), IntBinaryOperator.applyAsInt(int, int) et ToIntFunction<T>.applyAsInt(T).
Combine des règles dans un ordre précis
import java.util.function.Function;
import java.util.function.Predicate;
class Rules {
public static void main(String[] args) {
Predicate<String> isLong = w -> w.length() > 5;
Predicate<String> longB = isLong.and(w -> w.startsWith("b"));
System.out.println(longB.test("banana")); // true
System.out.println(isLong.negate().test("kiwi")); // true
Function<Integer, Integer> twice = x -> x * 2;
Function<Integer, Integer> inc = x -> x + 1;
System.out.println(twice.andThen(inc).apply(5)); // 11
System.out.println(twice.compose(inc).apply(5)); // 12
}
}
and exige que les deux conditions soient vraies, or qu’au moins l’une le soit, et negate inverse la réponse. Predicate.not(p) nie aussi un prédicat. andThen exécute d’abord la fonction receveuse ; compose exécute d’abord son argument. Ici, la première chaîne calcule 10 puis 11, tandis que la seconde calcule 6 puis 12.
Transmets une règle à une méthode
static <T> List<T> keep(List<T> list, Predicate<T> test) {
List<T> kept = new ArrayList<>();
for (T x : list) {
if (test.test(x)) kept.add(x);
}
return kept;
}
keep(List.of(4, 9, 12, 15), n -> n % 3 == 0) donne [9, 12, 15]. Grâce à T, la méthode est réutilisable avec différents types d’éléments ; grâce au prédicat, son comportement de sélection est réutilisable.
Pour une liste modifiable [3, 8, 1, 10, 6], removeIf(n -> n < 5) laisse [8, 10, 6]. replaceAll(n -> n * 2) donne ensuite [16, 20, 12]. Ces méthodes modifient la liste ; forEach accepte un consommateur pour des actions telles que l’affichage. Copie le résultat de List.of(...) dans une ArrayList avant d’utiliser des opérations qui le modifient.
Pour les Map, forEach((key, value) -> ...) prend un BiConsumer. merge(word, 1, Integer::sum) stocke 1 pour un nouveau mot et incrémente un effectif existant. computeIfAbsent(key, k -> new ArrayList<>()) crée une valeur pour une clé absente ou associée à null et renvoie la valeur obtenue. Ces méthodes réunissent la recherche et la règle de mise à jour.
Points clés
- Choisis une interface fonctionnelle selon la forme de ses entrées et de son résultat.
andThenexécute d’abord la fonction receveuse ;composeexécute d’abord son argument.removeIfteste un prédicat ;replaceAllapplique un opérateur unaire.mergecombine une valeur existante de la Map avec une valeur fournie.computeIfAbsentpeut construire et stocker une valeur absente d’une Map.- Les spécialisations pour types primitifs fonctionnent directement avec des valeurs primitives.
2.3 Capture des variables et écris des références de méthodes
Une expression lambda peut utiliser des informations de la méthode qui l’entoure. Lorsque son corps ne fait qu’appeler une méthode existante, une référence de méthode exprime souvent le même comportement plus directement.
Capture une variable locale
int limit = 10;
Predicate<Integer> small = n -> n < limit;
System.out.println(small.test(7)); // true
L’expression lambda capture limit. Une variable locale capturée doit être final ou effectivement finale : elle reçoit une valeur une seule fois et n’est jamais réaffectée. Ajouter limit = 20; n’importe où après cette première affectation empêche la compilation de la capture, même si la réaffectation apparaît avant l’expression lambda.
La règle s’applique à la variable locale, pas à tout l’état de l’objet qu’elle référence. Ceci compile :
StringBuilder log = new StringBuilder();
Runnable action = () -> log.append("ran");
action.run();
System.out.println(log); // ran
log référence toujours le même constructeur de chaîne. La mise à jour d’un champ de l’objet environnant est aussi autorisée, mais des modifications cachées peuvent rendre le comportement difficile à suivre.
Essayer numbers.forEach(n -> total += n) avec une variable locale int total = 0 échoue, car cela modifie la variable capturée. Utilise une boucle lorsque la tâche consiste en une mise à jour, ou calcule directement un résultat avec numbers.stream().mapToInt(n -> n).sum().
Lis les quatre formes de références
Une référence de méthode utilise ::, sans liste d’arguments. L’interface fonctionnelle cible fournit les entrées et le résultat attendu.
| Type | Référence | Expression lambda équivalente |
|---|---|---|
| Méthode statique | Integer::parseInt | s -> Integer.parseInt(s) |
| Méthode d’instance liée | System.out::println | x -> System.out.println(x) |
| Méthode d’instance non liée | String::length | s -> s.length() |
| Constructeur | ArrayList::new | () -> new ArrayList<>() |
Pour la référence liée, l’objet receveur est déjà fixé : le PrintStream référencé par System.out. Pour la référence non liée, le premier argument de l’interface fonctionnelle devient l’objet receveur. Ainsi, String::compareToIgnoreCase se développe en (a, b) -> a.compareToIgnoreCase(b) : la première chaîne est l’objet receveur et la seconde est l’argument de la méthode.
Adapte la référence à un type cible
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
class References {
public static void main(String[] args) {
Function<String, Integer> parse = Integer::parseInt;
Function<String, Integer> length = String::length;
Supplier<List<String>> fresh = ArrayList::new;
System.out.println(parse.apply("42")); // 42
System.out.println(length.apply("stream")); // 6
System.out.println(fresh.get()); // []
}
}
Une référence de constructeur peut aussi prendre des arguments. Student::new convient à une Function<String, Student> lorsqu’un constructeur correspondant prend une String. Le type cible détermine quelle surcharge de constructeur est sélectionnée.
Student::getName désigne un accesseur d’instance appelé sur chaque étudiant fourni ; il ne nécessite pas d’accesseur statique. Écris une expression lambda lorsqu’un travail supplémentaire est nécessaire, comme dans s -> s.getPoints() * 2 ou w -> w.length() > 5. Écrire String::length() est incorrect : la référence désigne un comportement sans l’invoquer à cet endroit.
Points clés
- Une variable locale capturée doit être finale ou effectivement finale.
- Une référence finale peut tout de même référencer un objet mutable.
- Une référence liée fixe son objet receveur ; une référence non liée le prend dans le premier argument.
- Les références de constructeurs utilisent
ClassName::new. - Les références de méthodes n’ont pas de parenthèses d’appel.
3Ordonne des objets avec Comparator
Les mêmes objets peuvent nécessiter plusieurs ordres utiles : les noms par ordre alphabétique, les points par ordre décroissant ou les groupes avec un critère de départage selon les scores. Les comparateurs séparent ces règles de l’ordre naturel de la classe. Une comparaison correcte doit respecter la cohérence des signes et des relations d’ordre. Les méthodes de construction rendent les clés visibles, mais la position d’une inversion et la gestion des égalités ou des valeurs nulles déterminent toujours le résultat.
3.1 Écris un comparateur et utilise-le pour trier
L’ordre naturel d’un objet n’est pas toujours celui dont une tâche a besoin. Un Comparator<T> distinct te permet de trier les mêmes objets selon différentes règles sans modifier leur classe.
Lis le signe de compare
compare(a, b) renvoie un nombre négatif lorsque a vient en premier, zéro lorsque les deux ont le même rang et un nombre positif lorsque a vient après b. Seul le signe compte. Un comparateur ne renvoie pas de booléen.
Comparable<T> appartient à la classe et fournit son ordre naturel par compareTo. Un comparateur est une valeur distincte transmise à une méthode de tri. Les chaînes ont un ordre naturel des caractères, mais un comparateur peut les trier par longueur.
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
class SortWords {
public static void main(String[] args) {
List<String> words = new ArrayList<>(List.of("ccc", "a", "bb"));
Comparator<String> byLength =
(a, b) -> Integer.compare(a.length(), b.length());
words.sort(byLength);
System.out.println(words); // [a, bb, ccc]
}
}
Le comparateur extrait les longueurs et compare les nombres. words.sort modifie la liste modifiable et renvoie void ; il ne renvoie pas de nouvelle liste. Trier le résultat de List.of(...) lève une UnsupportedOperationException ; l’exemple en fait donc d’abord une copie sous forme d’ArrayList.
Compare les nombres sans erreur
Utilise Integer.compare ou Double.compare. Une soustraction peut inverser le signe à cause d’un dépassement de capacité : 2_000_000_000 - (-2_000_000_000) vaut -294 967 296 en tant que int. Le plus grand nombre est alors placé à tort en premier dans l’ordre croissant. Integer.compare(2_000_000_000, -2_000_000_000) renvoie correctement 1.
Convertir par transtypage une différence de valeurs double en int fait aussi perdre de l’information. (int) (1.2 - 0.7) vaut 0, bien que la première valeur soit plus grande. Double.compare(1.2, 0.7) renvoie un résultat positif.
Garde un ordre cohérent
Inverser les entrées doit inverser le signe de la comparaison. Si a précède b et b précède c, a doit précéder c. Les valeurs dont la comparaison donne zéro doivent avoir le même rang par rapport à toute autre valeur. Enfreindre ces règles peut donner un ordre incorrect ou amener le tri à lever une IllegalArgumentException avec « Comparison method violates its general contract! ». Renvoyer uniquement 0 ou 1 ne permet pas d’exprimer les deux sens.
Les tris de listes et les tris de tableaux d’objets avec comparateur en Java sont stables : les éléments à égalité conservent leur ordre initial. Pour les étudiants Ada 85, Ben 72, Chen 85, Dara 90 et Eli 64, le tri par points croissants donne Eli, Ben, Ada, Chen, Dara. Ada reste avant Chen parce qu’ils sont à égalité et qu’Ada apparaissait en premier.
Les exemples d’étudiants utilisent cette classe et une liste modifiable :
class Student {
private final String name;
private final String group;
private final int points;
Student(String name, String group, int points) {
this.name = name;
this.group = group;
this.points = points;
}
String getName() { return name; }
String getGroup() { return group; }
int getPoints() { return points; }
}
List<Student> students = new ArrayList<>(List.of(
new Student("Ada", "A", 85), new Student("Ben", "B", 72),
new Student("Chen", "A", 85), new Student("Dara", "B", 90),
new Student("Eli", "A", 64)));
students.sort(Comparator.comparingInt(Student::getPoints));
students.forEach(s -> System.out.println(s.getName()));
// Eli, Ben, Ada, Chen, Dara, each on a separate line
Choisis une méthode de tri
list.sort(cmp) et Collections.sort(list, cmp) modifient une liste modifiable. list.sort(null) demande l’ordre naturel. Arrays.sort(array, cmp) accepte un tableau d’objets tel que Integer[] ; il n’existe pas de surcharge avec comparateur pour un int[].
L’ordre naturel des chaînes compare les unités de code UTF-16. Les majuscules A–Z précèdent les minuscules a–z, donc ["banana", "Cherry", "apple"] devient [Cherry, apple, banana]. String.CASE_INSENSITIVE_ORDER donne [apple, banana, Cherry] pour ces mots. Choisis explicitement la règle de comparaison du texte lorsque les majuscules et les minuscules ne doivent pas déterminer l’ordre.
Points clés
- Un comparateur renvoie un résultat de comparaison signé, pas un booléen.
Comparablefournit l’ordre naturel ; un comparateur fournit un ordre distinct.- Utilise les méthodes de comparaison numérique pour éviter les dépassements de capacité et la troncature.
- Les tris stables conservent l’ordre initial des éléments à égalité.
- Le tri d’une liste modifie une liste modifiable et renvoie
void. - Les tableaux de types primitifs ne peuvent pas recevoir de comparateur.
3.2 Combine des comparateurs avec comparing, thenComparing et reversed
Un ordre fondé sur plusieurs clés se lit plus facilement comme une suite de décisions : comparer la clé principale, puis comparer une seconde clé uniquement si la première donne une égalité. Les méthodes de construction de comparateurs expriment directement ces décisions.
Extrais une clé et départage les égalités
Pour un Student doté de getName(), getGroup() et getPoints(), Comparator.comparing(Student::getName) ordonne par nom. comparingInt(Student::getPoints) extrait un int sans le convertir en objet enveloppe.
Comparator<Student> ranking =
Comparator.comparingInt(Student::getPoints)
.reversed()
.thenComparing(Student::getName);
Ce comparateur classe d’abord les points les plus élevés, puis les noms par ordre croissant en cas d’égalité de points. Pour Ada (A, 85), Ben (B, 72), Chen (A, 85), Dara (B, 90), Eli (A, 64), il donne Dara, Ada, Chen, Ben, Eli. La comparaison des noms ne s’exécute qu’en cas d’égalité de points.
Place reversed au bon niveau
reversed() inverse tout ce qui a été construit avant lui. Inverser le classement après thenComparing inverserait aussi les noms au sein de chaque groupe à égalité de score. Pour classer les groupes par ordre croissant et les points par ordre décroissant dans chaque groupe, inverse uniquement le comparateur de points :
Comparator<Student> byGroup =
Comparator.comparing(Student::getGroup)
.thenComparing(
Comparator.comparingInt(Student::getPoints).reversed());
Le résultat est Ada, Chen, Eli, Dara, Ben : le groupe A précède le groupe B ; dans A, 85 précède 64. Placer .reversed() à la fin inverserait à la fois l’ordre des groupes et celui des points par rapport à la chaîne précédente.
Choisis les règles pour le texte et les valeurs nulles
Comparator.naturalOrder() suit Comparable ; reverseOrder() inverse cet ordre. Fournis un comparateur de clés lorsqu’une clé nécessite un traitement particulier, comme dans Comparator.comparing(Student::getName, String.CASE_INSENSITIVE_ORDER).
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
class SortNullable {
public static void main(String[] args) {
List<String> words = new ArrayList<>();
words.add("b");
words.add(null);
words.add("a");
words.sort(Comparator.nullsFirst(Comparator.naturalOrder()));
System.out.println(words); // [null, a, b]
}
}
Encadre la comparaison au niveau où apparaît la valeur nulle. nullsLast autour du comparateur d’étudiants entier gère les objets étudiants nuls. Un extracteur de nom qui renvoie null nécessite un comparateur prenant en charge les valeurs nulles pour la clé du nom elle-même. Les comparateurs d’ordre naturel et les comparateurs de clés ordinaires ne gèrent pas automatiquement les clés nulles.
Trie les entrées d’une Map et résous les difficultés d’inférence
Une HashMap ne garantit aucun ordre de rencontre. Crée un flux à partir de ses entrées et choisis explicitement un ordre :
stock.entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.forEach(System.out::println);
Pour rice=12, oats=3 et flour=7, ce code affiche rice=12, flour=7, oats=3, sur des lignes distinctes. Il ordonne les entrées du flux sans réorganiser stock. Ajoute thenComparing(Map.Entry.comparingByKey()) si les valeurs à égalité nécessitent un ordre prévisible des clés.
Le <String, Integer> explicite aide l’inférence dans l’appel chaîné. De même, Comparator.comparing(s -> s.getName()).reversed() peut conduire à déduire que s est un Object. Utilise Student::getName ou (Student s) -> s.getName() pour fournir le type manquant. Un comparateur nommé peut être réutilisé pour le tri de listes, les opérations sur les flux sorted, min et max, ou une Map ou un ensemble ordonné.
Points clés
comparingextrait une clé ; les méthodes pour types primitifs évitent de convertir les clés numériques en objets enveloppes.thenComparingn’est utilisé que lorsque la comparaison précédente donne une égalité.reversed()inverse le comparateur déjà construit.- Gère les valeurs nulles là où elles apparaissent : objets entiers ou clés extraites.
- Trier un flux d’entrées ne réordonne pas sa Map source.
4Traite des collections avec des flux
Un pipeline de flux transforme une collection en une suite de décisions de traitement. Suis les valeurs et leurs types : un filtre les sélectionne, une transformation les modifie et une opération terminale produit la réponse. L’exécution paresseuse signifie que certains éléments peuvent ne jamais être examinés, tandis que le tri nécessite toute l’entrée. Les flux numériques et les valeurs optionnelles rendent les types de résultats explicites, y compris lorsqu’aucune valeur ne subsiste ou qu’un total numérique est trop grand.
4.1 Construis un pipeline de flux et comprends son exécution
Un flux décrit comment traiter une séquence de valeurs. Il lit une source, fait passer les éléments par des opérations intermédiaires et se termine par une opération terminale qui produit un résultat ou effectue une action.
Repère les trois parties
import java.util.List;
class CountPasses {
public static void main(String[] args) {
List<Integer> points = List.of(72, 45, 90, 58, 81);
long passed = points.stream()
.filter(p -> p >= 60)
.count();
System.out.println(passed); // 3
System.out.println(points); // [72, 45, 90, 58, 81]
}
}
points.stream() est la source. filter est une opération intermédiaire : elle renvoie un autre flux. count est l’opération terminale : elle renvoie un long. Les valeurs 72, 90 et 81 passent le filtre. La lecture par le flux n’a pas supprimé 45 ni 58 de points.
Choisis une source
Les collections proposent stream(). Une Map fournit des flux par entrySet(), keySet() ou values(). Arrays.stream(array) crée un flux des éléments d’un tableau ; un int[] produit un IntStream. Stream.of("a", "b", "c") fournit les valeurs énumérées.
IntStream.range(1, 5) contient 1, 2, 3, 4, la fin étant exclue. rangeClosed(1, 5) inclut 5 et sa somme vaut 15. Stream.iterate(1, n -> n * 2) n’a pas de fin fixe ; .limit(5).toList() collecte [1, 2, 4, 8, 16]. Il est essentiel de limiter le résultat lorsqu’on consomme cette séquence infinie.
Suis l’exécution paresseuse
Les opérations intermédiaires sont paresseuses. Le simple fait d’écrire names.stream().map(String::toUpperCase); n’effectue pas les transformations. Une opération terminale lance le traitement, et les éléments ne sont consommés qu’au besoin.
String first = java.util.stream.Stream.of("one", "two", "three", "four")
.filter(w -> w.length() > 3)
.map(String::toUpperCase)
.findFirst()
.orElse("none");
System.out.println(first); // THREE
Les deux premiers mots ne passent pas le filtre. "three" passe, devient "THREE" et satisfait findFirst. Le dernier mot n’est pas nécessaire. C’est un court-circuit : la réponse est connue avant que toute la source soit lue.
sorted a besoin de toute l’entrée avant de pouvoir émettre la première valeur triée. Certaines autres opérations avec état, comme distinct en mode séquentiel, peuvent émettre des valeurs au fur et à mesure que les entrées arrivent. La place d’une opération dans la chaîne influe sur la quantité de travail nécessaire.
Crée un nouveau flux pour un autre résultat
Après une opération terminale, le flux est consommé. Réutiliser ce flux lève généralement une IllegalStateException avec « stream has already been operated upon or closed ». Appelle de nouveau points.stream() pour un autre pipeline. Une collection est un stockage réutilisable ; un flux est un chemin de traitement unique.
Créer un flux ne copie pas ses éléments. Garde la source inchangée pendant son traitement. map produit des valeurs transformées ; il ne les réécrit pas dans la liste. Pour modifier volontairement une liste, des méthodes telles que replaceAll et removeIf expriment directement cette tâche. Utilise une opération par ligne afin que l’ordre du traitement et le type de résultat restent visibles.
Points clés
- Un pipeline comporte une source, des opérations intermédiaires et une opération terminale.
- Les opérations intermédiaires sont paresseuses.
- Le court-circuit peut éviter de lire le reste d’une source.
sorteddoit recevoir toute l’entrée avant d’émettre une sortie triée.- Un flux consommé doit être remplacé par un nouveau flux issu de la source.
- Créer un flux ne copie ni ne réécrit sa source.
4.2 Filtre, transforme et réordonne des éléments
Suis à la fois les valeurs et leur type après chaque opération : une expression lambda ultérieure reçoit l’élément actuel du flux, et non l’objet source d’origine.
Sélectionne avant de transformer
filter conserve les éléments qui satisfont un prédicat. map remplace chaque élément par une valeur calculée. Pour des étudiants dotés d’accesseurs pour le nom et les points, la sélection selon les points doit précéder la transformation en noms :
List<String> names = students.stream()
.filter(s -> s.getPoints() >= 80)
.map(Student::getName)
.sorted()
.toList();
Pour Ada 85, Ben 72, Chen 85, Dara 90 et Eli 64, ce code produit [Ada, Chen, Dara]. Avant map, le type des éléments est Student. Après cette opération, le type est String : un accesseur de points n’est donc plus disponible.
Passe à un flux de type primitif
Stream<Integer> contient des références vers des objets enveloppes et n’a pas de méthode sum(). mapToInt(Integer::intValue) produit un IntStream, qui fournit des opérations numériques. De même, mapToInt(String::length) mesure les longueurs des chaînes sous forme de valeurs primitives.
import java.util.List;
import java.util.stream.IntStream;
class Windows {
public static void main(String[] args) {
List<Integer> window = IntStream.rangeClosed(1, 10)
.skip(2)
.limit(3)
.boxed()
.toList();
System.out.println(window); // [3, 4, 5]
}
}
skip(2) retire 1 et 2 du flux. limit(3) conserve les trois valeurs suivantes. Inverser ces appels ne laisse que 3. boxed() revient à un Stream<Integer> afin que les éléments puissent être collectés en tant qu’objets. mapToObj crée d’autres objets à partir de valeurs primitives ; les flux de long et de double possèdent des opérations de conversion correspondantes.
Supprime les doublons et choisis un ordre
distinct utilise l’égalité des objets. ["map", "filter", "map", "sorted", "filter"] devient [map, filter, sorted], puis sorted() donne [filter, map, sorted]. Pour tes propres classes, implémente des méthodes equals et hashCode cohérentes si des valeurs de champs identiques doivent être considérées comme des doublons.
sorted() utilise l’ordre naturel. Fournis un comparateur pour un autre ordre ou pour des objets sans ordre naturel. Le tri d’un flux ordonné est stable. Trier les cinq étudiants par points décroissants, puis en prendre deux, sélectionne Dara et Ada : Ada précède Chen dans la liste d’origine alors qu’ils ont le même score. Appeler limit(2) avant le tri n’examinerait que les deux premiers étudiants de la liste d’origine.
Aplatis des séquences imbriquées
List<Integer> all = List.of(
List.of(1, 2), List.of(3), List.of(4, 5, 6))
.stream()
.flatMap(List::stream)
.toList();
System.out.println(all); // [1, 2, 3, 4, 5, 6]
Chaque liste interne devient un flux, et flatMap réunit ses éléments en un seul flux. Remplacer ici flatMap par map donne un Stream<Stream<Integer>> : un objet flux par liste interne, sans aplatissement.
peek transmet une valeur sans la modifier et peut l’afficher pour le débogage. Son action ne s’exécute que pour les éléments effectivement parcourus, et une implémentation peut ignorer une étape inutile, par exemple lors de l’optimisation de count. Ne l’utilise pas pour effectuer un travail indispensable. Pour obtenir une seule valeur maximale, utilise max(comparator) au lieu de trier tous les éléments et d’en prendre un.
Points clés
filterconserve le type des éléments ;mappeut le changer.- Les flux de types primitifs fournissent des opérations numériques absentes de
Stream<Integer>. - L’ordre de
skip,limitet du tri change les éléments sélectionnés. distinctsuit l’égalité ; le tri d’un flux ordonné préserve l’ordre des éléments à égalité.flatMapaplatit les flux produits à partir des éléments d’entrée.peeksert à l’inspection, pas aux effets indispensables du programme.
4.3 Réduis un flux à un résultat unique
Choisis l’opération terminale selon le résultat dont tu as besoin : un effectif, un nombre, une valeur de vérité, un élément ou une combinaison générale. Une entrée vide compte lorsqu’aucune valeur ne peut être choisie comme résultat.
Calcule un nombre
count() renvoie un long. Sur un flux de type primitif, sum() renvoie un nombre du type primitif du flux. Pour les points [72, 45, 90, 58, 81], filtrer les valeurs d’au moins 60 puis les additionner donne 243. La moyenne de ces trois valeurs est 81,0.
import java.util.List;
class PointResults {
public static void main(String[] args) {
List<Integer> points = List.of(72, 45, 90, 58, 81);
int sum = points.stream().filter(p -> p >= 60)
.mapToInt(Integer::intValue).sum();
double average = points.stream().filter(p -> p >= 60)
.mapToInt(Integer::intValue).average().orElse(0.0);
System.out.println(sum); // 243
System.out.println(average); // 81.0
}
}
Le second pipeline utilise un nouveau flux. Une somme vide vaut 0, mais une moyenne calculée sur une entrée vide n’a pas de valeur. average() renvoie un OptionalDouble, et orElse(0.0) fournit une valeur de repli choisie. Cette valeur par défaut est une décision du programme, pas la moyenne mathématique d’un ensemble vide.
Une somme de type int peut provoquer un dépassement de capacité. Additionner deux valeurs de 2 000 000 000 produit -294 967 296 en tant que int. Convertis avant d’additionner : mapToInt(...).asLongStream().sum() donne 4 000 000 000 en tant que long.
Gère une valeur ou son absence
Un Optional<T> contient une valeur ou aucune. Sur un flux d’objets, min, max, findFirst, findAny et une réduction sans élément neutre renvoient des valeurs optionnelles. points.stream().max(Integer::compare) donne Optional[90] ; l’affichage du conteneur inclut le texte qui entoure la valeur.
Utilise orElse pour une valeur de repli, orElseGet pour un fournisseur de repli, ifPresent pour une action ou map pour transformer une valeur présente. Filtrer les étudiants ayant plus de 95 points, transformer le premier résultat en nom et appliquer orElse("nobody") donne "nobody" pour les données des cinq étudiants. Appeler get() ou orElseThrow() sur une valeur optionnelle vide lève une NoSuchElementException.
Pose une question dont la réponse est vraie ou fausse
anyMatch demande si au moins une valeur satisfait le prédicat, allMatch si toutes les valeurs le satisfont et noneMatch si aucune ne le satisfait. Ces opérations peuvent s’arrêter dès que la réponse est connue. Sur une entrée vide, anyMatch est faux, tandis que allMatch et noneMatch sont vrais : aucune valeur ne contredit l’une ou l’autre de ces affirmations universelles.
Combine avec un élément neutre
Stream.of(3, 5, 7).reduce(0, Integer::sum) accumule 0, 3, 8, 15. Pour la multiplication, commence par 1 et utilise (a, b) -> a * b pour obtenir 105. Un élément neutre laisse une valeur inchangée pour l’opération ; 10 n’est pas un élément neutre pour l’addition et décalerait à tort le total à 25. L’accumulateur doit être associatif afin que le regroupement ne change pas le résultat.
Sans élément neutre, reduce(Integer::max) donne Optional[7] et une valeur optionnelle vide pour une entrée vide. forEach, lui, effectue une action et renvoie void ; il convient à l’affichage. Utilise une réduction ou une collecte lorsque la tâche consiste à produire une valeur de résultat.
Points clés
- Les effectifs utilisent
long; les résultats des flux numériques suivent leur type primitif. - Une somme vide vaut zéro ; une moyenne ou un extremum sur une entrée vide n’a pas de valeur.
- Utilise les opérations prévues pour le cas vide d’une valeur optionnelle avant de lire sa valeur.
- Sur un flux vide,
allMatchetnoneMatchsont vrais ;anyMatchest faux. - Une réduction nécessite un élément neutre et un accumulateur associatif.
- Convertis en
longavant d’additionner des valeurs dont la somme dépasse la plage deint.
5Collecte et exploite les résultats des flux
Un résultat de flux utile peut être une liste, un ensemble, une Map, une chaîne ou une synthèse pour chaque groupe. Le collecteur détermine ce qui est stocké et ce qui se passe lorsque des clés se répètent. Le regroupement combine des règles de sélection et des synthèses en aval ; le partitionnement fournit deux groupes booléens. Une tâche complète peut ensuite interroger la Map obtenue. Comparer ce pipeline à une boucle t’aide à choisir un code lisible et à séparer la construction du résultat des modifications de l’état extérieur.
5.1 Collecte les résultats dans des listes, des ensembles, des Map et des chaînes
Un collecteur transforme un flux en une structure de résultat utile. Détermine ce que le résultat doit prendre en charge : doublons, ordre, modifications ultérieures ou clés de Map en double. Les garanties diffèrent selon les collecteurs.
Choisis une liste ou un ensemble
stream.toList() renvoie une liste non modifiable dans l’ordre de rencontre. Ajouter ou supprimer des éléments lève une UnsupportedOperationException. collect(Collectors.toList()) collecte aussi une liste, mais ne garantit ni une implémentation particulière ni la possibilité de la modifier. Pour une liste que tu modifieras, demande-la explicitement :
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class Gather {
public static void main(String[] args) {
List<String> words = Stream.of("a", "b", "a")
.collect(Collectors.toCollection(ArrayList::new));
words.add("c");
System.out.println(words); // [a, b, a, c]
System.out.println(Stream.of("a", "b", "a")
.collect(Collectors.toSet()).size()); // 2
}
}
toSet supprime les doublons et ne garantit aucun ordre. toCollection(TreeSet::new) demande un ensemble trié. Une liste conserve les éléments en double ; choisis un ensemble lorsque le résultat représente des valeurs distinctes.
Assemble du texte
Collectors.joining(", ") assemble des chaînes avec un séparateur, sans séparateur final. Sa forme à trois arguments ajoute aussi un préfixe et un suffixe :
String names = Stream.of("Ada", "Chen", "Dara")
.collect(Collectors.joining(", ", "[", "]"));
System.out.println(names); // [Ada, Chen, Dara]
Les éléments doivent être des valeurs CharSequence, généralement des chaînes. Transforme les étudiants avec Student::getName ou les nombres avec String::valueOf avant de les assembler. La jointure renvoie une seule chaîne, et non une liste dont l’affichage serait simplement similaire.
Choisis les clés et les valeurs d’une Map
Collectors.toMap(keyFunction, valueFunction) calcule une entrée à partir de chaque élément. Avec les cinq étudiants Ada (A, 85), Ben (B, 72), Chen (A, 85), Dara (B, 90), Eli (A, 64), associer les noms aux points donne cinq entrées telles que Ada=85 et Eli=64.
Les clés de regroupement sont différentes : plusieurs étudiants appartiennent au même groupe. toMap(Student::getGroup, Student::getPoints) à deux arguments lève une IllegalStateException lorsqu’une clé se répète, même si les deux valeurs sont 85. Une Map nécessite une seule valeur résultante par clé, et cette forme ne fournit aucune règle pour la choisir.
Fournis un troisième argument pour fusionner les doublons :
Map<String, Integer> totals = students.stream()
.collect(Collectors.toMap(
Student::getGroup, Student::getPoints, Integer::sum));
Le groupe A reçoit 85 + 85 + 64 = 234 ; le groupe B reçoit 72 + 90 = 162. Remplacer Integer::sum par Math::max conserve le nombre de points le plus élevé par groupe, A=85 et B=90. Utilise la règle qui répond à la tâche, pas une première valeur choisie arbitrairement.
toMap ne garantit aucun ordre particulier pour la Map. Un quatrième argument peut demander un ordre, par exemple TreeMap::new pour des clés triées. toMap(Student::getName, Student::getPoints, (a, b) -> a, TreeMap::new) conserve la première valeur en cas de noms en double et trie par nom. Un collecteur évite aussi de créer une liste extérieure et de la modifier dans forEach ; le résultat est construit par l’opération conçue pour le rassembler.
Points clés
Stream.toList()est non modifiable.Collectors.toList()ne garantit pas une implémentation modifiable.- Utilise
toCollectionlorsqu’un type de collection précis est nécessaire. joiningrassemble du texte avec un séparateur, un préfixe et un suffixe facultatifs.toMapà deux arguments refuse les clés en double, même lorsque les valeurs associées sont égales.- Une fonction de fusion résout les cas de clés répétées dans une Map.
- Demande une fabrique de Map lorsque son ordre compte.
5.2 Regroupe et partitionne des données avec des collecteurs
Un résultat regroupé répond à une question pour chaque clé. Une partition répond à une question par oui ou par non avec deux groupes. Un collecteur en aval détermine ce que chaque groupe contient.
Regroupe selon une clé calculée
groupingBy(classifier) produit une Map qui associe chaque clé rencontrée à une liste de ses éléments. Regrouper ["fig", "kiwi", "pear", "plum", "apple", "mango", "banana"] selon la longueur des mots associe à la longueur 4 la liste [kiwi, pear, plum]. Les longueurs absentes n’ont pas d’entrée ; rechercher l’une d’elles renvoie null. Utilise donc getOrDefault(key, List.of()) lorsqu’une liste vide est utile.
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
class WordGroups {
public static void main(String[] args) {
List<String> words = List.of(
"fig", "kiwi", "pear", "plum", "apple", "mango", "banana");
Map<Integer, Long> counts = words.stream()
.collect(Collectors.groupingBy(
String::length, TreeMap::new, Collectors.counting()));
System.out.println(counts); // {3=1, 4=3, 5=2, 6=1}
}
}
La fonction de classification choisit des clés entières. TreeMap::new ordonne ces clés. counting() remplace la liste de chaque groupe par sa taille sous forme de Long. Le type du résultat est donc Map<Integer, Long>, et non une Map d’entiers vers des entiers.
Change ce que contient un groupe
Avec les étudiants Ada (A, 85), Ben (B, 72), Chen (A, 85), Dara (B, 90), Eli (A, 64), le regroupement selon Student::getGroup offre ces possibilités :
| Collecteur en aval | Valeur sous A | Type de valeur |
|---|---|---|
counting() | 3 | Long |
summingInt(Student::getPoints) | 234 | Integer |
averagingInt(Student::getPoints) | 78.0 | Double |
mapping(Student::getName, Collectors.toList()) | [Ada, Chen, Eli] | List<String> |
Un collecteur en aval travaille à l’intérieur de chaque groupe. Pour A, la moyenne est 234 divisé par 3, soit 78,0 ; celle de B est 81,0. maxBy et minBy stockent un Optional contenant l’extremum de chaque groupe. groupingBy seul ne garantit ni type de Map ni ordre des clés particulier ; sa forme avec fabrique de Map permet de choisir explicitement.
Répartis selon deux réponses
Map<Boolean, List<String>> partition = students.stream()
.collect(Collectors.partitioningBy(
s -> s.getPoints() >= 80,
Collectors.mapping(Student::getName, Collectors.toList())));
La liste associée à vrai est [Ada, Chen, Dara], et celle associée à faux est [Ben, Eli]. Les deux clés booléennes sont présentes, même si l’un des groupes ne contient aucun étudiant. Avec counting() en aval, les groupes vides contiennent 0 sous forme de Long. À l’inverse, groupingBy avec une fonction de classification booléenne ne crée que les clés rencontrées.
Traite la Map construite
Un résultat regroupé est une Map ordinaire. Parcours-la avec forEach((group, value) -> ...), ou crée un flux à partir de ses entrées. Pour les moyennes A=78.0 et B=81.0, averages.entrySet().stream().max(Map.Entry.comparingByValue()) renvoie un Optional<Map.Entry<String, Double>> contenant B=81.0. L’Optional contient l’entrée gagnante ; ce n’est pas la simple valeur 81,0. Une étape distincte peut transformer cette entrée en sa clé ou sa valeur et gérer le cas d’une Map vide.
Points clés
- Une fonction de classification choisit les clés des groupes ; un regroupement simple stocke des listes d’éléments.
- Un collecteur en aval change la forme du résultat de chaque groupe.
countingproduit unLong; les collecteurs de moyenne produisent unDouble.- Une partition contient toujours les deux clés booléennes.
- La recherche d’une clé de regroupement absente renvoie
null, pas un groupe vide.
5.3 Choisis entre flux et boucles pour réaliser une tâche complète
Une tâche complète de traitement de données comporte souvent deux étapes : résumer les entrées, puis interroger le résultat. Choisis les flux lorsque ces étapes se lisent naturellement comme des opérations standard, et les boucles lorsqu’un contrôle explicite rend le travail plus clair.
Calcule la moyenne des relevés, puis sélectionne les villes
Supposons qu’un Reading possède getCity() et getCelsius(). Voici les relevés, en degrés Celsius : Oslo 4,5, Lima 19,0, Oslo 6,0, Cairo 27,5 et Lima 21,0.
class Reading {
private final String city;
private final double celsius;
Reading(String city, double celsius) {
this.city = city;
this.celsius = celsius;
}
String getCity() { return city; }
double getCelsius() { return celsius; }
}
Crée la liste d’entrée avant chacune des deux implémentations :
List<Reading> readings = List.of(
new Reading("Oslo", 4.5), new Reading("Lima", 19.0),
new Reading("Oslo", 6.0), new Reading("Cairo", 27.5),
new Reading("Lima", 21.0));
Map<String, Double> averages = readings.stream()
.collect(Collectors.groupingBy(
Reading::getCity,
TreeMap::new,
Collectors.averagingDouble(Reading::getCelsius)));
List<String> warmCities = averages.entrySet().stream()
.filter(e -> e.getValue() > 15)
.map(Map.Entry::getKey)
.toList();
Le premier résultat est {Cairo=27.5, Lima=20.0, Oslo=5.25}. La moyenne d’Oslo est (4.5 + 6.0) / 2, soit 5,25. Celle de Lima est 20,0. Le second résultat est [Cairo, Lima] ; la TreeMap fournit les villes par ordre croissant. Filtre les moyennes après le regroupement : filtrer d’abord les relevés individuels répond à une autre question.
Pour le relevé individuel le plus élevé, utilise readings.stream().max(Comparator.comparingDouble(Reading::getCelsius)). Cette opération renvoie une valeur optionnelle contenant le relevé de Cairo, pas nécessairement la ville dont la moyenne est la plus élevée. Ces tâches peuvent donner des réponses différentes avec d’autres données.
Exprime le même calcul avec des boucles
Map<String, List<Double>> byCity = new TreeMap<>();
for (Reading r : readings) {
byCity.computeIfAbsent(r.getCity(), k -> new ArrayList<>())
.add(r.getCelsius());
}
Map<String, Double> averages = new TreeMap<>();
for (Map.Entry<String, List<Double>> e : byCity.entrySet()) {
double sum = 0;
for (double value : e.getValue()) sum += value;
averages.put(e.getKey(), sum / e.getValue().size());
}
System.out.println(averages);
// {Cairo=27.5, Lima=20.0, Oslo=5.25}
Ce sont deux implémentations possibles du même résultat ; chaque groupe est non vide, car il a été créé par un relevé. Le flux rend explicites le regroupement et la moyenne dans un seul collecteur. Les boucles montrent les listes intermédiaires et la somme.
Garde un traitement prévisible
Une expression lambda de pipeline devrait effectuer son calcul à partir de son entrée sans modifier l’état extérieur. Pour les points [72, 45, 90, 58, 81], filter(p -> p >= 60).toList() produit directement [72, 90, 81]. Ajouter des éléments à une liste extérieure dans forEach cache cette construction du résultat dans une action.
Ne modifie pas une source ArrayList dans son pipeline : ajouter des éléments depuis un forEach peut lever une ConcurrentModificationException. Collecte d’abord les nouveaux éléments séparément, puis ajoute-les une fois le traitement terminé. Les règles de type de Java peuvent autoriser la mutation d’un objet par une référence finale capturée ; cela ne rend pas pour autant cette mutation adaptée à un flux.
Choisis le déroulement le plus clair
Utilise une boucle lorsque tu as besoin d’indices adjacents, d’une condition d’arrêt compliquée ou de plusieurs mises à jour coordonnées. Utilise les flux pour exprimer lisiblement une sélection, une transformation et une collecte. La syntaxe des flux ne garantit aucune vitesse à elle seule. Donne un nom à un prédicat ou à un comparateur long, et découpe une longue chaîne à un résultat intermédiaire qui a un sens. Pour déboguer, inspecte avec peek ou collecte une étape dans une liste et affiche-la ; poursuis avec un nouveau flux issu de cette liste au lieu de réutiliser un pipeline consommé.
Points clés
- Regroupe avant de filtrer lorsque la condition concerne les moyennes des groupes.
- Le relevé individuel maximal et la moyenne de groupe maximale répondent à des questions différentes.
- Un collecteur exprime directement la construction du résultat.
- Évite les mises à jour extérieures et la mutation de la source dans le comportement du pipeline.
- Utilise des boucles lorsque des indices ou un contrôle coordonné rendent la tâche plus claire.
- Inspecte une étape collectée, puis utilise un nouveau flux pour la suite du travail.
Généricité et flux en Java : exemples
1Construis une API réutilisable qui garantit la sécurité des types
17 étapes
- 1IntroductionPréserve la relation entre les types
Un conteneur réutilisable doit garder la trace de ce que ses utilisateurs peuvent y placer et en retirer. Un paramètre de type porte cette relation dans une déclaration.
Tu vas construire une boîte, déplacer le choix du type vers une méthode, puis vérifier ce qui change lorsqu’un paramètre utilise un type joker. À chaque étape, demande-toi ce que le compilateur sait d’une valeur avant l’exécution du programme.
- 2ExplicationDéclare la boîte
Tnomme le type choisi par le code appelant dans cette classe :class Box<T> { private T value; Box(T value) { this.value = value; } T get() { return value; } void set(T value) { this.value = value; } }Le champ, l’entrée du constructeur, le résultat de l’accesseur et l’entrée du mutateur utilisent tous le même
T. La déclaration introduit un paramètre ; aucun type de valeur stockée particulier n’a encore été choisi. - 3ExempleUtilise un type concret
Crée une boîte et lis sa valeur :
Box<String> b = new Box<>("tea"); String drink = b.get(); b.set("water");Stringfournit l’argument de type. L’accesseur renvoie une chaîne sans transtypage, et le mutateur accepte des chaînes. Le diamant à droite demande à Java de déduire les arguments. Il ne supprime pas les vérifications ; omettre complètement les chevrons créerait une utilisation de type brut. - 4DéfiPrévois l’écriture rejetée
La
Box<T>générique possèdevoid set(T value). AprèsBox<Integer> b = new Box<>(8);, est-ce queb.set("nine")compile ? Explique à quel moment l’échec se produit.---reveal---
L’appel échoue à l’écriture parce que cette référence attend un
Integer, pas uneString. Aucune lecture ultérieure n’est nécessaire pour découvrir l’erreur. Le littéral 8 peut être converti enInteger; les arguments de type eux-mêmes doivent toujours être des types références, doncBox<int>est incorrect. - 5LienChoisis un type pour chaque appel
Une méthode utilitaire peut introduire son propre paramètre de type :
static <T> T last(List<T> list) { return list.get(list.size() - 1); }Le premier
<T>déclare le paramètre, et leTsuivant nomme le type de résultat. L’appeler sur[4, 8, 15]renvoie l’Integer15 ; l’appeler sur["x", "y"]renvoie la chaîne"y". La méthode nécessite une liste non vide, indépendamment de ses vérifications de type. - 6ConseilÉcris un témoin après le point
Java déduit généralement l’argument de type de la méthode à partir de la liste fournie. Un appel peut aussi l’indiquer :
String word = Lists.<String>last(words);Le témoin de type suit le point et précède
last. Une méthode statique dans une classe générique a toujours besoin de son propre paramètre :static <E> Box<E> of(E value). LeTde la classe appartient aux utilisations paramétrées des instances, tandis que la méthode statique appartient à la classe partagée. - 7DéfiRelie l’entrée et la sortie
Une méthode utilitaire doit renvoyer un élément d’une
List<E>sans imposer de transtypage écrit par le code appelant. Une personne écritstatic E first(List<E> xs). Quelle déclaration manque ?---reveal---
Ajoute
<E>avant le type de retour :static <E> E first(List<E> xs). Cela introduit un paramètre propre à la méthode et relie les éléments de son entrée à son résultat. ÉcrireObjectcomme résultat ferait perdre cette relation utile au lieu de la rétablir. - 8ExplicationRends la comparaison disponible
Un
Tsans restriction n’a pas d’opérationcompareToconnue. Une borne peut fournir cette garantie :<T extends Comparable<T>>Une méthode
largest(List<T>)peut alors conserver unT best, comparer chaquexàbestet remplacer ce dernier lorsquex.compareTo(best) > 0. Pour 3, 11 et 7, les meilleures valeurs sont 3, puis 11, puis 11. Le retour reste du type d’élément propre à la liste. - 9Point cléProtège une liste d’entiers
Un
Integerest unNumber, mais uneList<Integer>n’est pas uneList<Number>.Si cette affectation de liste était autorisée, le code utilisant la référence vers la liste de nombres pourrait ajouter un
Double. Le code qui détient la liste d’origine le lirait alors comme unInteger. L’invariance des types génériques empêche l’affectation afin que les lectures et les écritures respectent leurs garanties.ArrayList<Number>est accepté commeList<Number>, car son argument de type pour les éléments correspond toujours. - 10ExempleTransfère dans le sens autorisé
Un type joker avec extends permet de lire selon un type supérieur connu ; un type joker avec super permet d’insérer un type inférieur connu.
static <T> void append( List<? super T> dst, List<? extends T> src) { dst.addAll(src); }Avec une destination
List<Number>modifiable et une sourceList<Integer>, Java peut choisirIntegerpourT. Chaque élément de la source est acceptable pour la destination, sans prétendre que les arguments de type des deux listes sont identiques. - 11DéfiChoisis la lecture sûre
Un paramètre est de type
List<? super Integer>. Son premier élément peut-il être affecté directement à une variableInteger? Quel type est garanti à la place ?---reveal---
Seul
Objectest garanti. La liste effective peut être uneList<Number>ou uneList<Object>et contenir des valeurs existantes autres que des entiers. Ajouter un entier par ce type est sûr, mais cette autorisation d’écriture ne décrit pas tous les éléments déjà stockés. - 12ExplicationDistingue les vérifications des classes à l’exécution
L’effacement remplace les paramètres de type par leurs bornes, en utilisant
Objectlorsqu’aucune borne n’est indiquée. Java a déjà vérifié les utilisations génériques et insère les transtypages nécessaires lors des lectures.Une
Box<String>et uneBox<Integer>partagent la même classeBoxà l’exécution. Il n’existe pas de classe distincte pour une boîte d’entiers qu’un test à l’exécution pourrait identifier. La généricité préserve les relations entre types lors de la vérification du code source, ce qui explique pourquoi elle supprime de nombreux transtypages visibles sans spécialiser la classe pour chaque argument. - 13ExempleFournis le comportement de création
Un paramètre de type ne peut pas être instancié avec
new T(). Demande plutôt une fabrique au code appelant :static <T> T create(Supplier<T> factory) { return factory.get(); } List<String> xs = create(ArrayList::new);La référence choisit un constructeur de liste par l’intermédiaire du type fournisseur cible. Les tableaux génériques tels que
new T[10]sont également rejetés ; une collection telle queArrayList<T>convient généralement au stockage. - 14ConseilSuis l’avertissement de type brut
Une
Listbrute peut accepter"text"et 42 avec des avertissements d’appel non vérifié. Un transtypage ultérieur(String)échoue lorsqu’il atteint 42.Remplacer la déclaration par
List<String>déplace le problème à l’ajout incorrect. Un avertissement de type non vérifié signifie que le compilateur a perdu une garantie ; l’exécution peut tout de même réussir pour certaines données. Examine l’utilisation du type brut ou le transtypage qui a provoqué l’avertissement, car la lecture qui échoue finalement peut se trouver bien plus loin. - 15DéfiVérifie la surcharge après effacement
Une classe déclare
show(Set<String> xs)etshow(Set<Integer> xs). Pourquoi Java ne peut-il pas utiliser les arguments de type des éléments pour choisir une surcharge ?---reveal---
Les deux types de paramètres deviennent
Setaprès effacement ; les déclarations ont donc la même signature après effacement. Donne-leur des noms distincts ou utilise une opération commune. Un test à l’exécution d’unObjectcontreList<String>présente une limite apparentée : testeList<?>sans affirmer quel est son argument de type pour les éléments. - 16DéfiExamine une API de transfert réutilisable
Une API utilise une liste source brute, transtype chaque élément en
Numberet l’ajoute à une liste de nombres. Elle doit accepter des entiers et des nombres à virgule flottante comme sources. Quel paramètre source supprime ce transtypage tout en préservant des lectures sûres de nombres ?---reveal---
Utilise
List<? extends Number>. Chaque élément peut être lu comme unNumber, et les listes d’entiers comme de nombres à virgule flottante conviennent.List<Number>refuserait ces arguments de liste plus restreints. La destination doit être modifiable ; son seul type générique ne peut pas garantir que les écritures réussiront. - 17RécapitulatifLis une API à partir de ses garanties
Lorsque tu examines une déclaration générique, repère où chaque paramètre de type est introduit, quelles positions il relie et quelles opérations sa borne fournit.
Pour un transfert, identifie la source comme producteur et la destination comme consommateur. Pour une étape de création, cherche le comportement de construction fourni. Pour un avertissement ou une opération rejetée, demande-toi si les informations génériques ont été contournées ou si l’opération nécessiterait de connaître un type d’élément à l’exécution.
2Transforme un comportement en argument
14 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionCommence par la forme du comportement
- 2ExplicationTrouve la méthode à implémenter
- 3ExempleUtilise un bloc lorsqu’une décision le nécessite
- 4DéfiCorrige le résultat manquant
- 5ExplicationAssocie quatre tâches courantes à leurs interfaces
- 6ExempleObserve l’ordre de composition
- 7LienLaisse les collections exécuter la règle
- 8DéfiDistingue la suppression de la sélection
- 9ExplicationCapture une valeur qui n’est pas réaffectée
- 10ExempleIdentifie l’objet receveur dans la référence
- 11DéfiVérifie ce que final protège
- 12ExempleCompte avec une règle de mise à jour de Map
- 13DéfiCombine type cible et capture
- 14RécapitulatifSuis un appel à travers la règle
3Construis un ordre décision par décision
12 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionNomme l’ordre nécessaire à la tâche
- 2ExplicationLis un résultat de comparaison
- 3ExempleÉvite la soustraction dans les comparaisons numériques
- 4ExempleConserve l’ordre initial des scores égaux
- 5DéfiDistingue l’ordre naturel de l’ordre propre à une tâche
- 6ExplicationAjoute une règle uniquement pour les égalités
- 7Point cléInverse uniquement les décisions voulues
- 8ConseilGère les valeurs nulles au bon niveau
- 9DéfiInterprète l’inversion finale
- 10LienOrdonne les entrées de la Map
- 11DéfiRends prévisible l’ordre des entrées de Map à égalité
- 12RécapitulatifVérifie l’arbre des comparaisons
4Suis les valeurs dans un flux
16 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionSuis ensemble les valeurs et leurs types
- 2ExplicationRepère le résultat terminal
- 3ExempleArrête-toi au premier élément correspondant
- 4DéfiEffectue un autre calcul
- 5ExempleSélectionne les étudiants avant de les transformer en noms
- 6LienPasse aux opérations numériques
- 7ExempleAplatis avant de supprimer les doublons
- 8DéfiLis l’ordre des opérations
- 9ExempleCalcule une somme et une moyenne
- 10ExplicationDistingue l’absence d’une valeur
- 11DéfiGère un maximum absent
- 12Point cléVérifie les tests universels sur une entrée vide
- 13ExempleChoisis une valeur de départ neutre
- 14ConseilConvertis avant le dépassement de capacité
- 15DéfiCombine aplatissement, sélection et résultat
- 16RécapitulatifVérifie les cas limites du résultat
5Construis une synthèse de données utile
15 étapes
Enregistre cette unité pour suivre chaque Leçon Interactive pas à pas.
- 1IntroductionPars du résultat souhaité
- 2ExplicationExige explicitement les modifications ultérieures
- 3ExempleRésous les collisions avec une règle de fusion
- 4DéfiChoisis une valeur par clé répétée
- 5LienAssemble les valeurs sous forme de texte
- 6ExplicationChoisis une clé, puis un résultat de groupe
- 7ExempleRésume les données dans les groupes d’étudiants
- 8Point cléConserve les deux groupes d’une partition
- 9DéfiPrévois un groupe vide
- 10ExempleCalcule d’abord les moyennes par ville
- 11ExempleInterroge la synthèse construite
- 12ConseilRends le déroulement explicite lorsque cela aide
- 13DéfiProtège la source pendant le traitement
- 14DéfiTrouve le groupe gagnant sans erreur
- 15RécapitulatifVérifie l’ensemble de la tâche de traitement de données
Généricité et flux en Java : QCM
Série 1 : Utilise les notions de base10 Questions Pratiques
Question 1 sur 10
Box<T> générique possède T get() et void set(T value). Pour Box<String> label = new Box<>("north");, quel est le type de résultat déclaré de label.get() ?Afficher la réponseMasquer la réponse
String remplace le paramètre de classe T dans le résultat déclaré de l’accesseur. Le compilateur considère donc que l’appel renvoie une chaîne, sans transtypage écrit par le code appelant. L’effacement ne transforme pas le type du résultat en Object dans le code source ; la vérification à la compilation utilise toujours l’argument.Question 2 sur 10
Afficher la réponseMasquer la réponse
E avant son type de retour et le répète pour les éléments d’entrée et le résultat. Chaque appel possède ainsi son propre argument de type déduit. Écrire simplement E aux positions du paramètre et du retour ne le déclare pas : la version sans <E> échoue donc dans une classe utilitaire ordinaire.Question 3 sur 10
doubleValue() sur chaque élément. Elle doit accepter à la fois List<Integer> et List<Double>. Quel type de paramètre convient ?Afficher la réponseMasquer la réponse
Number : chaque élément lu peut donc être traité comme un Number. List<Number> ne peut pas accepter List<Integer>, car les types génériques sont invariants. Un type joker avec super autoriserait des types d’éléments plus larges, mais les lectures ne fourniraient que le type Object.Question 4 sur 10
Function<Integer, Integer> doit doubler son entrée à l’aide d’un corps sous forme de bloc. Quelle expression lambda convient ?Afficher la réponseMasquer la réponse
return x * 2; fournit le résultat entier requis par la fonction. Une expression lambda dont le corps est une expression pourrait simplement utiliser x -> x * 2, mais l’ajout d’accolades ne conserve pas ce retour automatique.Question 5 sur 10
int limit = 4; List<String> notes = new ArrayList<>(); Predicate<String> p = s -> { notes.add(s); return s.length() > limit; }; limit++;. Quelle correction respecte ces exigences ?Afficher la réponseMasquer la réponse
Question 6 sur 10
compare(a, b) ?Afficher la réponseMasquer la réponse
Question 7 sur 10
names.stream().filter(s -> s.length() > 2) et ne fait rien du flux renvoyé. Quand le prédicat est-il appelé ?Afficher la réponseMasquer la réponse
filter est une opération intermédiaire qui enregistre une étape de traitement paresseuse. Aucune opération terminale ne lance ici le parcours : le prédicat n’est donc pas appelé. Créer un flux ne copie ni ne filtre immédiatement la liste source, et une lecture ultérieure sans rapport de la liste n’exécute pas ce pipeline.Question 8 sur 10
reduce(0, (a, b) -> a * b). Quelle est la cause du résultat incorrect ?Afficher la réponseMasquer la réponse
Question 9 sur 10
add. Quelle expression terminale fournit explicitement une ArrayList ?Afficher la réponseMasquer la réponse
toCollection(ArrayList::new) demande une implémentation de liste connue et modifiable. Stream.toList() est non modifiable, et Collectors.toList() ne garantit pas la possibilité de modifier le résultat, même si une implémentation particulière le permet. Un collecteur d’ensemble change aussi la structure du résultat et la gestion des doublons.Question 10 sur 10
ArrayList<String> words appelle forEach(w -> words.add(w + "!")). Comment le programme doit-il ajouter sans erreur une copie transformée de chaque mot d’origine ?Afficher la réponseMasquer la réponse
ArrayList pendant que son flux la lit perturbe le parcours et peut lever une ConcurrentModificationException. Une autre référence pointe toujours vers la même source ; elle ne sépare pas les étapes de lecture et d’écriture.Série 2 : Choisis une implémentation sûre10 Questions Pratiques
Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.
Question 1 sur 10
Pair<String, Integer> p = new Pair<>("zone", 4);, quels sont le type de p.getFirst() et la valeur de p.getSecond() + 2 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 2 sur 10
List<? super Integer> sink. Quel est le type le plus précis garanti pour sink.get(0) ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
show(Set<Long> xs) et show(Set<String> xs). Pourquoi la compilation échoue-t-elle ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
Badge possède un constructeur Badge(String text). Une méthode accepte Function<String, Badge> factory. Quelle référence crée le même comportement que s -> new Badge(s) ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
["z", null, "a"]. Elle est triée avec Comparator.nullsLast(Comparator.reverseOrder()). Quel ordre obtient-on ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
s est utilisée pour s.count(), puis pour s.toList(). Comment le programme doit-il obtenir les deux résultats à partir d’une liste réutilisable data ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
Stream.of("mint", "ash", "mint", "elm").distinct().map(String::length).sorted().toList() ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Série 3 : Suis les types et les résultats10 Questions Pratiques
Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.
Question 1 sur 10
Box<Integer> contient 12. Le programme affecte int next = box.get() + 3;. Comment ce code peut-il compiler et donner 15 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 2 sur 10
pick appartient à la classe Items et déclare <T> T pick(List<T> xs). Comment écrit-on un argument de type String explicite lors d’un appel ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
T lors de l’effacement ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
int run(int x), une méthode par défaut reset() et une méthode statique help(). L’expression x -> x + 2 peut-elle l’implémenter ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
String et détermine si elle se termine par ".csv". Quelle interface standard convient à la condition attendue par filter ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
comparing(Item::getGroup).thenComparingInt(Item::getScore).reversed(). Quel ordre des noms obtient-on ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
IntStream.rangeClosed(2, 9).skip(3).limit(2).boxed().toList() ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
Stream.<Integer>empty(), quels sont les résultats d’appels distincts, chacun sur un nouveau flux, à anyMatch(n -> n > 0), allMatch(n -> n > 0) et noneMatch(n -> n > 0), dans cet ordre ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
toMap(Item::getCode, Item::getCount) rencontre deux éléments de code "R", ayant tous deux un effectif de 3. Que se passe-t-il ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
partitioningBy(x -> x > 10, Collectors.counting()). Quel résultat est garanti ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Série 4 : Suis la tâche complète10 Questions Pratiques
Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.
Question 1 sur 10
Slot<T> a besoin d’une fabrique statique qui crée un emplacement typé à partir de n’importe quelle valeur. Quelle déclaration évite d’utiliser le paramètre de la classe dans un contexte statique ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 2 sur 10
addAll. Elle doit copier des entiers dans une liste de nombres ou une liste d’objets et préserver la sécurité des types. Quelle signature convient ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
boolean check(String s) et boolean equals(Object other), sans autre méthode abstraite. Une expression lambda peut-elle l’implémenter, et pourquoi ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
String::compareToIgnoreCase lorsqu’elle est utilisée comme Comparator<String> ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
(int) (a - b). Que se passe-t-il lorsqu’il compare 3,6 à 3,1 ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
List.of(List.of("red"), List.of("blue", "gold")).stream().map(List::stream) ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
"reed", "oak", "fern", "elm", "cedar". Après regroupement par longueur avec counting() dans une TreeMap, quelle Map s’affiche ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Série 5 : Transpose les idées à de nouvelles tâches10 Questions Pratiques
Enregistre cette unité pour voir chaque réponse et t'entraîner sur chaque série.
Question 1 sur 10
List<? super Integer> dst, List<? extends Number> src et essaie dst.add(src.get(0)). Elle doit accepter des sources d’entiers ou de valeurs double et des destinations modifiables de nombres ou d’objets. Comment faut-il modifier ses paramètres ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 2 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 3 sur 10
Function<Integer, Integer> triple = x -> x * 3; et shift = x -> x + 2;, que renvoie triple.andThen(shift).apply(4) ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 4 sur 10
HashMap<String, Integer> contient oak=6, ash=6, elm=2. Quel comparateur d’entrées garantit des valeurs décroissantes et des clés croissantes en cas d’égalité ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 5 sur 10
Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 6 sur 10
Stream.of("ash", "cedar", "elm", "pine").filter(w -> w.length() > 3).map(String::length).findFirst()Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 7 sur 10
[2, 5], [] et [5, 8]. Que renvoie groups.stream().flatMap(List::stream).distinct().filter(n -> n > 3).mapToInt(n -> n).sum() ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 8 sur 10
values.stream().filter(n -> n > -8).max(Integer::compare).map(n -> n * 2).orElse(0) ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 9 sur 10
Sale possède une équipe et un nombre entier d’unités. Ignore les ventes dont le nombre d’unités est nul ou négatif, calcule la moyenne de chaque équipe à partir de ses ventes restantes et renvoie l’équipe ayant la moyenne la plus élevée ou « none » si l’entrée est vide. On suppose qu’aucune moyenne n’est à égalité. Quel pipeline convient ? Les méthodes de collecteurs sont importées statiquement.Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Question 10 sur 10
"an", "oak", "to", "elm", quelle est la valeur du groupe vrai obtenue avec partitioningBy(w -> w.length() == 2, mapping(String::toUpperCase, toList())) ?Afficher la réponseMasquer la réponse
Enregistre cette unité pour voir cette réponse.
Tu bloques sur ton propre exercice ? Aide aux Devoirs
Mots-Clés
A
Argument de type
String fait que l’accesseur d’une Box<String> renvoie du texte sans transtypage.Associativité
Autoboxing
5 à un constructeur qui attend un Integer.Avertissement de type non vérifié
add d’une liste brute indique une possible lecture ultérieure de type incorrect.B
BinaryOperator
BinaryOperator<Integer> tel que Integer::sum peut fusionner les effectifs d’une clé de Map répétée.Borne supérieure
<T extends Number> permet d’appeler doubleValue() sur un T.Bornes multiples
& : un argument de type doit satisfaire chaque borne, une éventuelle classe étant indiquée en premier.<T extends Number & Comparable<T>> exigent des comportements numériques et de comparaison.Boucle à indice
list.get(i) à list.get(i - 1) tout en mettant à jour deux compteurs.C
Capture de variable
limit environnante.Classe enveloppe
Integer rend List<Integer> valide, tandis que List<int> est rejeté.Classe générique
Box<T> permet de créer une boîte de chaînes et une boîte d’entiers à partir d’une seule déclaration.Clé en double
toMap à deux arguments même lorsque les deux valeurs associées sont 85.Collecteur
forEach.Collecteur en aval
counting() transforme une Map de listes par groupe en une Map d’effectifs de type Long.Comparateur
compare renvoie un résultat négatif, nul ou positif pour deux valeurs d’entrée.Comparator<String> peut ordonner des mots par longueur sans changer leur ordre naturel de chaînes.Comparateur prenant en charge les valeurs nulles
nullsFirst ou nullsLast.Composition de fonctions
andThen et compose indiquent des ordres d’appel opposés.twice.andThen(inc), ce qui donne 11 à partir de 5.Consumer
accept ne renvoie aucune valeur.Consumer<String> peut afficher chaque mot lorsqu’il est transmis à forEach d’une collection.Contexte statique
<E> au lieu d’utiliser le T de la classe.Corps sous forme d’expression
x + 1 permet à x -> x + 1 de renvoyer directement l’entier incrémenté.Corps sous forme de bloc
return a; et return b; pour implémenter une opération de maximum.Court-circuit
anyMatch de renvoyer vrai dès qu’un élément satisfait son prédicat.Critère de départage
D
Dépassement de capacité d’un entier
distinct
distinct supprime les éléments en double d’un flux selon l’égalité des objets, en conservant l’ordre de rencontre pour un flux ordonné.distinct avant de transformer des mots en longueurs peut laisser des longueurs répétées dans le résultat.E
Effacement des types
Object après vérification ; le compilateur insère les transtypages nécessaires.Box à l’exécution.Effectivement final
final.Effet de bord
Entrée de Map
Expression lambda
-> entre les deux.(a, b) -> a * b fournit la règle de multiplication d’une opération.Extracteur de clé
Student::getPoints construit un comparateur numérique d’étudiants avec comparingInt.É
Élément neutre
Évaluation paresseuse
findFirst de s’arrêter dès qu’un mot satisfaisant la condition a été trouvé et transformé.F
Fabrique de collections
ArrayList::new demande explicitement une liste qui autorise les ajouts ultérieurs.filter
filter conserve les éléments du flux dont le prédicat est vrai, en préservant leur type et en écartant les autres valeurs.filter sur les points doit s’exécuter avant la transformation des étudiants en noms s’il a besoin de l’accesseur de points.flatMap
flatMap transforme chaque entrée en un flux et combine les éléments de ces flux en une seule séquence de sortie.flatMap utilisant List::stream transforme des listes d’entiers imbriquées en un seul flux d’entiers.Flux
points peut compter les valeurs qui satisfont un test tout en laissant la liste inchangée.Fonction de classification
String::length place tous les mots de quatre lettres sous la même clé entière.Fonction de fusion
Math::max conserve la plus grande quantité lorsqu’une clé de stock apparaît deux fois.Function
T en un résultat de type R par apply.Function<String, Integer> peut transformer chaque mot en sa longueur avant le regroupement.G
groupingBy
groupingBy construit une Map associant les clés rencontrées produites par une fonction de classification à des listes ou à des synthèses en aval des éléments correspondants.groupingBy avec averagingInt stocke une moyenne de type double par groupe d’étudiants.I
Inférence de types
last(words) de produire une String lorsque words est une liste de chaînes.Interface fonctionnelle
Object.Operation permet au code appelant de fournir son calcul avec une expression lambda.Interface générique
Container<T> peut garder l’entrée de put liée au résultat de take.IntStream
int doté d’opérations numériques telles que la somme et la moyenne, ainsi que de conversions vers des objets.IntStream issu de mapToInt peut additionner des points sans d’abord collecter de valeurs enveloppées.Invariance
List<Integer> comme List<Number>, ce qui la protège contre l’écriture de valeurs double.Inversion d’un comparateur
reversed(), y compris les critères de départage existants.J
joining
joining combine des séquences de caractères en une seule chaîne, avec éventuellement du texte entre les éléments et autour du résultat entier.joining avec une virgule comme séparateur ne produit pas de virgule après le dernier nom.L
limit
limit conserve au maximum le nombre demandé d’éléments au début d’un flux ordonné, ce qui permet d’ignorer les entrées ultérieures.limit avant le tri sélectionne des éléments au début de la séquence d’origine, pas les valeurs les plus élevées de l’ensemble.Liste non modifiable
Stream.toList() lève une exception si le code essaie d’y ajouter un élément.M
map
map remplace chaque élément d’entrée par un résultat calculé ; elle peut donc changer le type des éléments du flux.map utilisant String::length transforme des mots en valeurs entières enveloppées, pas en flux de type primitif.mapping
mapping transforme des éléments avant de transmettre leurs résultats à un autre collecteur, souvent dans un groupe ou une partition.mapping peut extraire les noms des étudiants dans chaque groupe avant de les collecter dans une liste.Méthode générique
last renvoie une chaîne pour une liste de chaînes et un entier pour une liste d’entiers.N
Non-interférence
ArrayList source.O
Opérateur diamant
<> dans un appel de constructeur générique ; il demande au compilateur de déduire les arguments de type tout en conservant les vérifications de type.new ArrayList<>() garde le type de la liste déduit de son contexte.Opération de vérification d’un prédicat
anyMatch, mais vrai pour allMatch.Opération intermédiaire
filter attend une opération terminale avant de tester les éléments.Opération terminale
count renvoie un long et empêche de réutiliser le même flux.Optional
Optional issu d’un maximum après filtrage peut être vide ; une valeur de repli gère donc le cas absent.OptionalDouble
OptionalDouble issu de average() peut fournir la valeur de repli choisie 0,0 grâce à orElse.Ordre de rencontre
findFirst à choisir le premier nom correspondant dans une liste source.Ordre naturel
Comparable.compareTo et qui est utilisé lorsqu’aucun comparateur distinct n’est fourni."Cherry" avant le mot en minuscules "apple".P
Paramètre de type
T donne au champ et à l’accesseur d’une boîte le même type choisi par le code appelant.partitioningBy
partitioningBy répartit les éléments selon un prédicat et renvoie toujours les deux clés booléennes, même si l’un des groupes est vide.partitioningBy peut renvoyer une liste true vide lorsqu’aucun étudiant n’atteint le seuil testé par son prédicat.peek
peek transmet les éléments sans les modifier tout en exécutant une action d’inspection pour les valeurs parcourues ; ses appels peuvent être ignorés.peek utilisé pour afficher des valeurs n’est pas fiable pour un travail indispensable, car l’exécution peut omettre son étape.Pipeline
stream à count en passant par filter produit un nombre d’éléments correspondants de type long.Predicate
boolean par test.Predicate<String> transmis à removeIf désigne les mots correspondants à supprimer.R
Réduction
Référence de constructeur
ClassName::new pour fournir un comportement de création d’objet, avec des arguments déterminés par l’interface cible.Student::new peut transformer un nom en étudiant grâce à un constructeur correspondant.Référence de méthode
::, en s’adaptant aux entrées et au résultat d’une interface fonctionnelle cible.Integer::parseInt fournit un comportement d’analyse à une fonction qui transforme une chaîne en entier.Référence de méthode liée
System.out::println affiche en utilisant cet objet de sortie particulier.Référence de méthode non liée
String::length appelle length() sur chaque chaîne fournie.Règle PECS
Résultat intermédiaire
S
Signature après effacement
show(Set) est commune aux tentatives de surcharge utilisant Set<String> et Set<Long>.skip
skip écarte le nombre demandé d’éléments au début d’un flux ordonné et transmet les éléments restants.skip(2) sur la plage de 1 à 10, bornes incluses, la prochaine valeur émise est 3.Spécialisation pour un type primitif
ToIntFunction<String> fournit directement des longueurs à mapToInt.Supplier
get sans argument fournit une valeur de son type de résultat.Supplier<List<String>> utilisant ArrayList::new peut fournir une nouvelle liste à chaque appel.T
Témoin de type
Lists.<String>last(words) indique explicitement le type choisi pour la méthode.Transitivité
Tri stable
Type brut
List xs peut autoriser une écriture incorrecte qu’une List<String> rejetterait.Type cible
Predicate<String> fait de s -> s.isEmpty() un test booléen sur une chaîne.Type joker à borne inférieure
? super Integer désigne un type inconnu qui est le type indiqué ou l’un de ses supertypes.List<Number> modifiable.Type joker à borne supérieure
? extends Number désigne un type inconnu qui est le type indiqué ou l’un de ses sous-types.Type joker non borné
? sans borne ; il représente un argument inconnu et ne permet de lire les éléments que comme des Object.List<?> permet à une méthode d’affichage de consulter la taille d’une liste sans connaître son type d’élément.U
UnaryOperator
UnaryOperator<String> peut mettre chaque mot en majuscules par replaceAll sur une liste modifiable.- 1Premiers programmes Java
- 2Conditions et boucles en Java
- 3Méthodes Java
- 4Listes et tableaux en Java
- 5Classes et objets en Java
- 6Références et égalité en Java
- 7HashMap et collections en Java
- 8Héritage et polymorphisme en Java
- 9Exceptions et fichiers en Java
- 10Paquetages et tests unitaires Java
- 11Généricité et flux en Java
À propos de cette unité
Crée ton propre Matériel d'Étude
Transforme tes notes, diapos et documents en Guides d'Étude, Questions Pratiques, Flashcards et 6 autres outils.