Paramétrage du comportement du code
enum Département {
EIE, IIM, MME, PSO
}
public static List<ElèvePops> filtreElèvesIIM(List<ElèvePops> élèves) {
List<ElèvePops> élèvesIIM = new ArrayList<>(); // ❶
for (ElèvePops élève : élèves) {
if (élève.getSpécialité().equals(Département.IIM)) { // ❷
élèvesIIM.add(élève);
}
}
return élèvesIIM;
}
public static List<ElèvePops> filtreElèvesParSpécialité(List<ElèvePops> élèves, Département spécialité) { // ❶
List<ElèvePops> élèvesFiltrés = new ArrayList<>();
for (ElèvePops élève : élèves){
if (élève.getSpécialité().equals(spécialité)) { // ❷
élèvesFiltrés.add(élève);
}
}
return élèvesFiltrés;
}
on a donc amélioré le moyen d'obtenir ce filtrage :
List<ElèvePops> élèvesIIM = filtreElèvesParSpécialité(tousElèves, Département.IIM);
public static List<ElèvePops> filtreElèvesMinNombreECTSValidés(List<ElèvePops> élèves, int nombreMinimalECTSValidés) { // ❶
List<ElèvePops> élèvesFiltrés = new ArrayList<>();
for (ElèvePops élève : élèves) {
if (élève.getNombreECTSValidés() >= nombreMinimalECTSValidés) { // ❷
élèvesFiltrés.add(élève);
}
}
return élèvesFiltrés;
}
DRY : Don't Repeat Yourself
List<ElèvePops> élèvesFiltrés = filtreElèves(tousElèves, Département.IIM, 50, Département.ESSONNE, ...);
public interface PrédicatElèvePops {
public boolean test(ElèvePops élève);
}
ce qui permet de définir un certain nombre de classes de prédicats utiles, ex. :
public class PrédicatElèveIIM implements PrédicatElèvePops {
public boolean test(ElèvePops élève) {
return élève.getSpécialité().equals(Département.IIM);
}
}
PrédicatElèvePops
, qui peuvent donc être choisis à l'exécutionPrédicatElèvePops
❶, et partager le même code d'itération sur la collection pour tous les filtres public static List<ElèvePops> filtreElèves(List<ElèvePops> élèves, PrédicatElèvePops filtre) { // ❶
List<ElèvePops> élèvesFiltrés = new ArrayList<>();
for (ElèvePops élève : élèves) {
if (filtre.test(élève)) {
élèvesFiltrés.add(élève);
}
}
}
return élèvesFiltrés;
}
List<ElèvePops> élèvesBoursiers = filtreElèves(tousElèves, new PrécicatElèveBoursier());
on est ici tenu de passer un objet (de type PrédicatElèvePops
) pour au final transmettre une expression booléenne au travers d'un objet qui implémente la méthode test
List<ElèvePops> élèvesIIM = filtreElèves(
tousElèves,
new PrédicatElèvePops() {
public boolean test(ElèvePops élève) {
return élève.getSpécialité().equals(Département.IIM);
}
} );
List<ElèvePops> élèvesIIM = filtreElèves(
tousElèves,
(Elève élève) -> élève.getSpécialité().equals(Département.IIM)
);
public interface Prédicate<T> {
boolean test(T t);
}
en paramétrant également la méthode de filtrage :
public static <T> List<T> filtre(List<T> liste, Predicate<T> prédicat) {
List<T> listeFiltrée = new ArrayList<>();
for (T e: liste) {
if (prédicat.test(e)) {
listeFiltrée.add(e);
}
}
return listeFiltrée;
}
ce qui permet alors de filtrer tout ce que l'on souhaite, en fournissant une expression lambda adaptée, ex. :
List<ElèvePops> élèvesIIM = filtre(
tousElèves,
(Elève élève) -> élève.getSpécialité().equals(Département.IIM)
);
List<JeuVidéo> jeuxNonInstallés = filtre(
bibliothèqueJeu,
(Jeu jeu) -> jeu.nonInstallé()
);
Comparator
Collection.sort
une instance de java.util.Comparator<T>
de définition :public interface Comparator<T> {
int compare(T o1, T o2);
}
tousElèves.sort(new Comparator<ElèvePops>() {
public int compare(ElèvePops e1, ElèvePops e2) {
return e1.getNomPrénom().compareTo(e2.getNomPrénom());
}
});
tousElèves.sort( (ElèvePops e1, ElèvePops e2) -> e1.getNomPrénom().compareTo(e2.getNomPrénom()) );
Une expression lambda est une notation compacte d'une fonction anonyme qui peut être transmise pour paramétrer du code
Syntaxe d'une expression lambda :
(paramètres) -> expression
(paramètres) -> { instructions; }
Une expression lambda avec une seule instruction (retournant void
), bien qu'elle ne soit pas une expression, peut être utilisée sans les accolades, ex: () -> System.out.println("une instruction")
;
(List<ElèvePops> élèves) -> élèves.isEmpty()
() -> new Examen()
(ElèvePops e) -> { System.out.println(e.toString()); }
(Adresse a) -> a.getPays()
(int a, int b) -> a * b
(ElèvePops e1, ElèvePops e2) -> e1.getNomPrénom().compareTo(e2.getNomPrénom())
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
@FunctionalInterface
est recommandée pour permettre au compilateur de s'assurer que l'intention est bien respectée.default
?les expressions lambda permettent de fournir l'implémentation de la méthode abstraite d'une interface fonctionnelle de manière compacte (inline) : on les manipule comme des instances d'une implémentation concrète d'une interface fonctionnelle.
L'API standard définit un certain nombre d'interfaces fonctionnelles particulières (dans le package java.util.function
)
Interfaces importantes :
Predicate<T>
Consumer<T>
Supplier<T>
Function<T, R>
Predicate<T>
Predicate<T>
définit une méthode abstraite qui accepte un objet d'un type générique et retourne un booléen@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
public <T> List<T> filtre(List<T> liste, Predicate<T> prédicat) {
List<T> résultat = new ArrayList<>();
for (T t: liste) {
if (prédicat.test(t)) {
results.add(t);
}
}
return results;
}
Predicate<String> prédicatChaîneNonVide = (String s) -> !s.isEmpty();
List<String> chaînesNonVides = filtre(chaînes, prédicatChaîneNonVide);
Présence dans l'API d'interfaces fonctionnelles spécifiques aux types primitifs (IntPredicate
, LongPredicate
, DoublePredicate
) ex.
public interface IntPredicate {
boolean test(int t);
}
IntPredicate nombresImpairs = (int v) -> v % 2 != 0;
BiPredicate<T, U>
Consumer<T>
Consumer<T>
définit une méthode abstraite qui prend un objet d'un type générique et ne retourne pas de résultat (traitement sur l'objet)@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
Variantes :
IntConsumer
, LongConsumer
, DoubleConsumer
BiConsumer<T, U>
,Exemple d'utilisation :
public <T> void forEach(List<T> liste, Consumer<T> c) {
for(T t: liste) {
c.accept(t);
}
}
forEach( Arrays.asList("iim", "eie", "pso", "mme"), (String s) -> System.out.println(s) );
Supplier<T>
Supplier<T>
définit une méthode abstraite qui ne prend pas de paramètre et qui retourne un objet d'un type générique@FunctionalInterface
public interface Supplier<T> {
T get();
}
Variantes : BooleanSupplier
,IntSupplier
, LongSupplier
, DoubleSupplier
Exemple d'utilisation :
Supplier<Integer> générateurAléatoire = () -> new Random().nextInt(100);
// ...
int randomNumber = générateurAléatoire.get();
Function<T, R>
Function<T, R>
définit une méthode qui prend un objet d'un type générique et retourne un autre objet d'un type générique (correspondance (mapping) entre objets)@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
public <T, R> List<R> map(List<T> liste, Function<T, R> fonction) {
List<R> résultat = new ArrayList<>();
for (T t: liste) {
résultat.add(fonction.apply(t));
}
return résultat;
}
List<Integer> liste = map( Arrays.asList("iim", "eie", "pso", "mme"), (String s) -> s.length() );
IntFunction<R>
, IntToDoubleFunction
, IntToLongFunction
, ...ToIntFunction<T>
, ToDoubleFunction<T>
, ToLongFunction<T>
BiFunction<T, U, R>
, ToIntBiFunction<T, U>
, ...@FunctionalInterface
public interface TelleInterfaceFonctionnelle {
void telleMéthode(Param p) throws TelleExceptionSousContrôle;
}
Function<A, B> fonction = (A a) -> {
try {
return a.getB();
}
catch(CheckedException ce) {
throw new RuntimeException(ce);
}
};
Bien sûr si deux interfaces fonctionnelles possèdent une méthode qui prend des paramètres de mêmes types et retournent le même type, ex.
Comparator< Etudiant> comp1 = (Etudiant e1, Etudiant e2) -> e1.getID().compareTo(e2.getID());
BiFunction<Etudiant, Etudiant, Integer> biFonction = (Etudiant e1, Etudiant e2) -> e1.getID().compareTo(e2.getID());
Par exemple, étant donnée l'interface fonctionnelle :
public interface PrédicatElèvePops {
public boolean test(ElèvePops élève);
}
et la méthode :
public static List<ElèvePops> filtreElèves(List<ElèvePops> élèves, PrédicatElèvePops filtre)
l'appel de la méthode suivant :
List<ElèvePops> élèvesIIM = filtreElèves(
tousElèves,
(Elève élève) -> élève.getSpécialité().equals(Département.IIM)
);
peut omettre le type du paramètre de l'expression lambda :
List<ElèvePops> élèvesIIM = filtreElèves(
tousElèves,
élève -> élève.getSpécialité().equals(Département.IIM)
);
_
(_, _) -> System.out.println("...")
Outre les paramètres d'expressions lambda, elles peuvent être utilisées pour :
for
final
( effectively final
)final
donc par exemple, on peut capturer dans le corps de l'expression lambda ❶ ci-dessous la variable locale final département
:
final Département département = Département.IIM;
PrédicatElèvePops filtre = (Elève élève) -> élève.getSpécialité().equals(département); // ❶
final
?Une variable qui n'aurait été au plus affectée qu'une seule fois (qu'elle soit marquée final
ou non).
Ici département
l'est bien :
Département département = Département.IIM;
PrédicatElèvePops filtre = (Elève élève) -> élève.getSpécialité().equals(département);
mais là non (le compilateur voit la réaffectation ❶) :
Département département = Département.IIM;
PrédicatElèvePops filtre = (Elève élève) -> élève.getSpécialité().equals(département);
département = Département.MME; // ❶
final
?final
) : elles peuvent réaliser des fermetures sur des valeurs, et non sur des variables.final this
.Type::méthode
l'expression lambda (Etudiant étudiant) -> étudiant.getID()
est donc ramenable à Etudiant::getID
(Etudiant étudiant) -> étudiant.getID()
Etudiant::getID
(étudiant, ue) -> étudiant.valideUE(ue)
Etudiant::valideUE
(ue) -> this.valideUE(ue)
this::valideUE
static
(arguments) -> Classe.méthodeStatique(arguments)
Classe::méthodeStatique
Math::cos
(argument0, autresArguments) -> argument0.méthodeInstance(autresArguments)
Classe::méthodeInstance
Etudiant::getID
(arguments) -> expression.méthodeInstance(arguments)
expression::méthodeInstance
élève::getID
(arguments) -> new Classe(arguments)
Classe::new
Function< Integer, SalleDeCours>
❸List< Integer> capacités = Arrays.asList(25, 25, 50);
List< SalleDeCours> salles = map(capacités, SalleDeCours::new); // ❶
List< SalleDeCours> map(List< Integer> capacités, Function<Integer, SalleDeCours> constructeur) { // ❸
List< SalleDeCours> résultat = new ArrayList< >();
for (Integer capacité : capacités) {
résultat.add(constructeur.apply(capacité)); // ❷
}
return résultat;
}
Comparator
Comparator
fondé sur une Function
qui utilise une valeur des objets pour leur comparaison :
Comparator<Etudiant> comparateur = Comparator.comparing(Etudiant::getAgeEnJours);
Comparator.comparing(Etudiant::getAgeEnJours).reversed()
Comparator.comparing(Etudiant::getAgeEnJours).reversed()
Comparator.comparing(Etudiant::getAgeEnJours).thenComparing(Etudiant::getID)
Predicate
negate
, and
et or
Predicate< Etudiant> étudiantsIIM = (Elève élève) -> élève.getSpécialité().equals(Département.IIM);
Predicate< Etudiant> étudiantsNonIIM = étudiantsIIM.negate();
Predicate< Etudiant> étudiantsNonIIMAnglais = étudiantsNonIIM
.and(étudiant -> !étudiant.mobilitéOK())
.or(étudiant -> étudiant.scoreTOEIC < 500);
Function
compose
: calcule tout d'abord la fonction passée en paramètre (g(x)
), puis la fonction utilisée (f(x)
), soit f(g(x))
Function< Integer, Integer> f = x -> x + 1;
Function< Integer, Integer> g = x -> x * 2;
Function< Integer, Integer> h = f.compose(g);
int résultat = h.apply(1); // f(g(1)) = f(2) = 3
andThen
:calcule tout d'abord la fonction utilisée (f(x)
), puis la fonction passée en paramètre (g(x)
), soit g(f(x))
Function< String, String> corrigeTexte = TexteUtils::corrigeTexte;
Function< String, String> transformations =
corrigeText
.andThen(TexteUtils::simplifieTexte)
.andThen(TexteUtils::ajouteAnnotations);
Beaucoup de détails d'implémentation : collections intermédiaires ❶, parcours explicite de collections ❷, paramétrage de comportement par classe abstraite ❸
List< Elève> élèvesSansTOEIC = new ArrayList<>(); // ❶
int scoreValidation = Pops.getScoreValidationTOEICActuel();
for(Elève élève : tousElèves) { // ❷
if (élève.getScoreTOEIC() < scoreValidation) {
élèvesSansTOEIC.add(élève);
}
}
Collections.sort(élèvesSansToeic, new Comparator< Elève>() { // ❸
public int compare(Elève e1, Elève e2) {
return Integer.compare(e1.getScoreTOEIC(), e2.getScoreTOEIC());
}
});
List< String> dettesTOEIC = new ArrayList<>(); // ❶
for (Elève élève : élèvesSansTOEIC) { // ❷
dettesTOEIC.add(élève.getNomComplet() + " - distance au seuil de validation : " + (scoreValidation - élève.getScoreTOEIC()) );
}
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
int scoreValidation = Pops.getScoreValidationTOEICActuel();
List< String> dettesTOEIC = tousElèves.stream() // ❹
.filter(élève -> élève.getScoreTOEIC() < scoreValidation) // ❶
.sorted(comparing(Elève::getScoreTOEIC())) // ❷
.map(élève -> élève.getNomComplet() + " - distance au seuil de validation : " + (scoreValidation - élève.getScoreTOEIC())) // ❸
.toList(); // ❺
java.util.stream.Stream
Stream
, permettant l'enchaînement des traitements, possiblement de manière paresseuse (lazy)java.lang.IllegalStateException
) :Stream< String> stream = Arrays.asList("EIE", "IIM", "MME", "PSO").stream();
stream.forEach(System.out::println); // ❶
stream.forEach(System.out::println); // ❷
void
, ex. forEach
)collect
: réduit la stream pour obtenir une collection count
: retourne le nombre d'éléments d'une stream (long
)forEach
: applique une expression lambda pour chaque élément d'une streamfilter
: filtre les éléments d'une stream à l'aide de Predicate<T>
(retourne boolean
), retourne Stream<T>
limit
: conserve un certain nombre d'éléments, retourne Stream<T>
distinct
: consever les éléments uniques, retourne Stream<T>
map
: établit une correspondance avec les éléments d'une stream à l'aide de Function<T, R>
(retourne R
), retourne Stream<R>
sorted
: tri les éléments d'une stream à l'aide de Comparator<T>
(retourne int
), retourne Stream<T>
record Elève(String name, int toeicScore) { }
List<Elève> élèves = Arrays.asList(
new Elève("E1", 500),
new Elève("E2", 700),
new Elève("E3", 200),
new Elève("E4", 600),
new Elève("E5", 100),
new Elève("E6", 800)
);
List< String> résultat = élèves.stream()
.filter( e -> e.toeicScore() >= 600 )
.map( e -> e.name + " " + e.toeicScore())
.limit(2)
.toList();
résultat.stream().forEach(e -> System.out.println(e));
toList
est invoquéereturn
) la valeur attendue ❷List< String> résultat = élèves.stream()
.filter(e -> { // ❶
System.out.println("filter : " + e);
return e.toeicScore() >= 600; // ❷
})
.map( e -> { // ❶
System.out.println("map : " + e);
return e.name + " " + e.toeicScore(); // ❷
})
.limit(2)
.toList();
List<Elève> élèves = Arrays.asList(
new Elève("E1", 500),
new Elève("E2", 700),
new Elève("E3", 200),
new Elève("E4", 600),
new Elève("E5", 100),
new Elève("E6", 800)
);
et on obtient :
filter : Elève[name=E1, toeicScore=500]
filter : Elève[name=E2, toeicScore=700]
map : Elève[name=E2, toeicScore=700]
filter : Elève[name=E3, toeicScore=200]
filter : Elève[name=E4, toeicScore=600]
map : Elève[name=E4, toeicScore=600]
E2 700
E4 600
limit(2)
permet à toList()
de faire une passe optimiséefilter
: filtre les éléments d'une stream à l'aide de Predicate<T>
(retourne boolean
), retourne Stream<T>
List<Elève> élèvesAvecToeic = élèves.stream()
.filter(Elève::aSonToeic)
.toList();
distinct
: filtre une stream pour ne conserver que des éléments uniques (se fonde sur les méthodes hashcode
et equals
du type des éléments)List<Integer> scoresAuToeic = élèves.stream()
.map(e -> e.toeicScore())
.distinct()
.sorted()
.toList();
takeWhile
: conserve les éléments d'une stream tant que Predicate<T>
est vrai, retourne Stream<T>
dropWhile
: conserve tous les éléments d'une stream après qu'unPredicate<T>
est faux, retourne Stream<T>
limit(nombre)
: conserve au plus les nombre
premiers éléments d'une streamskip(nombre)
: conserve tous les éléments d'une stream après les nombre
premiers map
: établit une correspondance avec les éléments d'une stream à l'aide de Function<T, R>
(retourne R
), retourne Stream<R>
flatMap
: établit une correspondance avec les éléments d'une stream avec une autre stream puis concatène les streams résultantes en une unique streamflatMap
Elève
dispose d'une méthode List<Cours> getCoursSuivis()
Stream< List< Cours>> stream = élèves.stream() // Stream< Elève>
.map(élève -> élève.getCoursSuivis()); // Stream< List< Cours>>
stream.forEach(cours -> System.out.println(cours));
Stream<List<Elève>>
, sur lequel on ne peut pas directement extraire la liste des cours suivis (sans répétition), ex.[CS101, CS203, CS440, CS601]
[CS101, CS201, CS440, CS605]
[CS101, CS201, CS202, CS402, CS711]
...
flatMap
remplace chaque valeur d'une stream, puis concatène les différentes streams en une seule (flattening) : on peut ici utiliser List::stream
pour obtenir une stream pour chaque List<Cours>
Stream< List< Cours>> stream = élèves.stream() // Stream< Elève>
.map(élève -> élève.getCoursSuivis()) // Stream< List< Cours>>
.flatMap(List::stream) // Stream< Cours>
.sorted() // on suppose que Cours est Comparable< Cours>
.distinct();
stream.forEach(cours -> System.out.println(cours));
[CS101, CS201, CS202, CS203, CS402, CS440, CS601, CS605, CS711]
anyMatch
: indique si au moins un élément d'une stream correspond à un prédicatallMatch
: indique si tous les éléments d'une stream correspondent à un prédicatnoneMatch
: indique si aucun élément d'une stream ne correspond à un prédicatfindAny
: extrait un élément quelconque d'une streamfindAny
pourrait ne pas produire de résultat (si la stream est vide) : que faire ?findAny
retourne Optional<T>
, ce qui permet d'invoquer ifPresent(Consumer<T> c)
pour exécuter un bloc si une valeur est effectivement retournée, ex.
élèves.stream()
.filter(e -> e.nom.startsWith("E1"))
.findAny()
.ifPresent(_ -> System.out.println("OK"));
reduce
Optional< Integer> min = nombres.stream()
.reduce( (e1, e2) -> e1 < e2 ? e1 : e2 );
ou avec une référence de méthode :
Optional< Integer> min = nombres.stream()
.reduce(Integer::min);
Optional<T>
(si la stream est vide) :Optional< Integer> somme = nombres.stream()
.reduce( (e1, e2) -> e1 + e2 );
ou surcharge qui prend une valeur initiale :
Optional< Integer> somme = nombres.stream()
.reduce(0, Integer::sum);
Stream<String> stream = Stream.of("EIE", "IIM", "MME", "PSO");
Stream.empty()
Stream<String> user = Stream.ofNullable(System.getProperty("user");
(retourne Stream.empty()
le cas échéant)Arrays.stream(tableauEntiers)
java.nio.file.Files
qui retournent des streams, ex.long nombreMotsUniques = 0;
try(Stream<String> lignes = Files.lines(Paths.get("fichier.txt"))) {
nombreMotsUniques = lignes
.flatMap(ligne -> Arrays.stream(ligne.split(" ")))
.distinct()
.count();
}
catch (IOException) { ... }
try-with-resource
?Parce que les streams implémentent Autocloseable
.
iterate
qui produit itérativement de nouvelles valeurs indéfiniment (possibilité de combiner avec limit(n)
), ex.
Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);
IntStream.iterate(n -> n < 10, n -> n + 2).forEach(System.out::println);
generate
qui prend une expression lambda de type Supplier<T>
, ex.
Stream.generate(Math::random).limit(10).forEach(System.out::println);
Collector
, transmise à collect
, applique une fonction de transformation aux éléments de la stream et accumule les résultats dans une collection
toList()
Collectors.maxBy
, Collectors.minBy
Collectors.summingInt
(Collectors.SummingLong
...), Collectors.averagingInt
, Collectors.summarizingInt
Collectors.summarizingInt
fournit un objet de type IntSummaryStatistics
qui donne accès aux valeurs somme, moyen, minimum et maximum sur une stream d'entiers, ex. : IntSummaryStatistics s = élèves.stream().collect(summarizingInt(Eleve::getScoreToeic));
Collectors.joining
(utilisation efficace en interne d'un objet StringBuilder
)
String tousElèves = élèves.stream().map(Elèves::getNom).collect(joining(", "));
Collectors.reducing
(dont un argument agrège deux éléments du même type en un seul)
Optional<Elève> élèveAvecLeMeilleurScoreAuToeic = élèves.stream().collect(reducing((e1, e2) -> e1.getScoreToeic() > e2.getScoreToeic() ? e1 : e2));
collect
et reduce
La méthode collect
est conçue pour modifier un accumulateur pour calculer son résultat, de façon compatible avec une traitement parallèle par les streams.
groupingBy
fondée sur une fonction de classification
Map<Département, List<Elève> élèvesParDépartement = élèves.stream().collect(groupingBy(Elève::getDépartement));
public enum SituationMobilité { VALIDE, EN_COURS, A_VENIR}
Map<SituationMobilité, List<Elève>> élèvesParSituationMobilité = élèves.stream()
.collect(groupingBy(
élève -> { // ❶
if (élève.mobilitéEffectuée()) return SituationMobilité.VALIDE;
else if (élève.mobilitéEnCours()) return SituationMobilité.EN_COURS;
else return SituationMobilité.A_VENIR;
}
));
groupingBy
, ex.Map<Département, Map<SituationMobilité, List<Elève>> élèvesParDépartementEtSituationMobilité = élèves.stream()
.collect(groupingBy(Elève::getDépartement),
groupingBy( // ❶
élève -> {
if (élève.mobilitéEffectuée()) return SituationMobilité.VALIDE;
else if (élève.mobilitéEnCours()) return SituationMobilité.EN_COURS;
else return SituationMobilité.A_VENIR;
}
)
);
groupingBy
tous types de collecteur
Map<Département, Long> nombresDElevesParDépartement = élèves.stream().collect(groupingBy(Elèves::getDépartement, counting())) ;
Boolean
)partitioningBy
fondée sur une fonction de partitionnement
Map<Boolean, List<Elève> élèvesAvecMobilitéValidée = élèves.stream().collect(partitioningBy(Elève::modilitéEffectuée));
filter
de conserver tous les éléments, pas uniquement ceux qui correspondent à un prédicatparallel()
long sommeEnSéquentiel(long n) {
return Stream.iterate(1L, i -> i + 1)
.limit(n)
.reduce(0L, Long::sum);
}
long sommeEnParallèle(long n) {
return Stream.iterate(1L, i -> i + 1)
.limit(n)
.parallel() // ❶
.reduce(0L, Long::sum);
}
Runtime.getRuntime().availableProcessors()
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "4");
L'intérêt d'une solution parallèle n'est pas toujours important ou positif. La mesure de la performance d'une exécution peut reposer pour les langages de la JVM sur des micro-benchmarks guidés par des annotations avec par exemple Java Microbenchmark Harness (JMH). Un tel outil permet de ne pas intégrer les temps de démarrage, de ne pas mesurer d'effets liés au ramasse-miette, ou encore de permettre la garantie de compilation du code à la volée.
IntStream
) évitent de coûteux autoboxing / unboxingfindFirst
qui repose sur l'ordre des éléments est coûteux, alors que findAny
le sera beaucoup moinslogger.log(Level.WARNING, construitMessage());
public void log(Level niveau, Supplier< String> fournisseurMessage)
logger.log(Level.WARNING, () -> construitMessage());
Le patron Template porte sur des situations où l'on souhaite pouvoir adapter localement un algorithme. Cela peut typiquement être représenté au travers de méthodes abstraites, qui seront implémentées dans des sous-classes qui préciseront leur stratégie propres, ex.
abstract class École {
public void inscrireÉlève(int id) {
Élève e = getÉlève(id);
inscrireÉlève(e);
}
abstract void inscrireÉlève(Élève e);
}
public void inscrireÉlève(int id, Consumer< Élève> inscrireÉlève) {
Élève e = getÉlève(id);
inscrireÉlève.accept(e);
}
école.inscrireÉlève(237,
(Élève e) -> e.message("Merci " + e.getNom() + " de venir chercher le laisser-passer A38") );
Le patron Factory vise à masquer au client d'une classe ses détails d'instanciation, ex.
class DocumentFactory {
public Document produireDocumentReglémentaire(String nomDocument) {
return switch (name) {
case "règlement intérieur" -> new DocumentRèglementIntérieur();
case "charte web" -> new DocumentCharteWeb();
default -> throw new IllegalArgumentException("Type de document inconnu : " + nomDocument);
};
}
}
Supplier<T>
pour la création de chaque type de documents, qui peuvent donc être organisés dans des map, ex.Map< String, Supplier< Document>> map = new HashMap<>();
map.put("règlement intérieur", DocumentRèglementIntérieur::new);
map.put("charte web", DocumentCharteWeb::new);
// …
class DocumentFactory {
public Document produireDocumentReglémentaire(String nomDocument) {
Supplier< Document> supplier = map.get(nomDocument);
if (supplier != null) return supplier.get();
throw new IllegalArgumentException("Type de document inconnu : " + nomDocument);
}
}