@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
Simplifie l’écriture des classes internes anonymes
Java 8+
Interface qui comporte une méthode unique
Hors private
, default
et static
De préférence annotée @FunctionalInterface
La méthode (donc l’interface) peut être assimilée à une fonction
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
Comparator<String> c1 = new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareToIgnoreCase(s2);
}
};
Comparator<String> c2 = (String s1, String s2) -> {
return s1.compareToIgnoreCase(s2);
};
Le type peut être omis
Comparator<String> c = (s1, s2) -> {
return s1.compareToIgnoreCase(s2);
};
Si le bloc est une simple expression :
Suppression du bloc
Omission du mot-clé return
Omission du ;
final
Comparator<String> c = (s1, s2) -> s1.compareToIgnoreCase(s2);
Quand une expression appelle une méthode existante :
Forme abrégée <Type>
+ ::
+ <méthode>
Comparator<String> c = String::compareToIgnoreCase;
Si il n’y a qu’un unique argument :
Suppression des parenthèses
Function<Integer,Integer> f = i -> i + 1;
Soit une liste de Person
Calculer la moyenne de l’âge des personnes
List<Person> persons = ...
double sum = 0.0;
for (Person person: persons) {
sum += person.getAge();
}
double average = sum / persons.size();
Beaucoup de boilerplate code
Focus sur le comment, pas sur le quoi
A l’inverse du tri, sort()
Capitaliser les noms de famille
Conserver uniquement les adultes
Combinaison de cas d’utilisation
etc.
Un stream est un pipeline de fonctions.
Stream de départ
Opérations intermédiaires → *
Opérations terminales → 1
Peut-être infini
Retourne un stream
Lazily executed (à la demande)
Retourne un type concret
Ou produit des effets de bord
Eagerly executed (lors de l’appel)
Démarre le pipeline
map()
filter()
limit()
skip()
distinct()
sorted()
Transforme un stream de types T en un autre stream de types R
Via une instance de Function<T, R>
persons
.stream()
.map(new Function<Person,String>() {
@Override
public String apply(Person person) {
return person.getFirstName();
}
})
.map(new Function<String,String>() {
@Override
public String apply(String name) {
return name.toUpperCase();
}
});
persons
.stream()
.map(person -> person.getFirstName())
.map(name -> name.toUpperCase());
persons
.stream()
.map(Person::getFirstName)
.map(String::toUpperCase);
Transforme un stream de taille x en un autre stream de taille y < x
Via une instance de Predicate
persons
.stream()
.filter(person -> person.getAge() >= 18);
Transforme un stream de taille x en un autre stream de taille y < x
En conservant les n premiers éléments
persons
.stream()
.limit(15);
Transforme un stream de taille x en un autre stream de taille y < x
En omettant les n premiers éléments
persons
.stream()
.skip(15);
Transforme un stream de taille x en un autre stream de taille y < x
En omettant les doublons
persons
.stream()
.distinct();
Transforme un stream d’éléments en un stream d’éléments ordonnés
Via une instance de Comparator<T>
Ou selon l’ordre naturel des éléments
persons
.stream()
.sorted((p1, p2) -> p1.getAge() - p2.getAge());
persons
.stream()
.sorted(Comparator.comparingInt(Person::getAge));
collect()
forEach()
reduce()
Regroupe les éléments d’un stream dans une collection concrète
Via une instance de Collector<T, A, R>
stream.collect(Collectors.toList());
stream.collect(Collectors.toSet());
stream.collect(Collectors.toMap(
person -> person.getId(),
person -> person));
stream.collect(Collectors.toMap(
Person::getId,
Function.identity()));
Invoque des effets de bord sur les éléments d’un stream
Via une instance de Consumer<T>
stream.forEach(person -> {
System.out.println(person);
});
stream.forEach(System.out::println);
Aggrège les éléments d’un stream dans un élément unique
Via une instance de BinaryOperator<T>
stream.mapToInt(Person::getAge).average();
Depuis une collection existante
Depuis un tableau existant
Depuis des éléments
Depuis une fonction
List<Person> persons = ...
Stream<Person> stream = persons.stream();
Person[] persons = ...
Stream<Person> stream = Arrays.stream(persons);
Stream<String> stream = Stream.of("one", "two", "three");
Stream<String> stream = Stream.generate(() -> "X");
Stream<Integer> stream = Stream.iterate(0, i -> i + 1);
Tous les éléments du stream vérifient le prédicat
Aucun élément du stream ne vérifie le prédicat
Au moins 1 élément du stream vérifie le prédicat
Renvoie un élément quelconque du stream
Renvoie le premier élément du stream (s’il est ordonné)
Permet d’accéder aux élément du stream sans le modifier
Exemple : log l’élément
Assemble les éléments de n collections dans un stream unique
Comment récupérer tous les produits commandés ?
Stream<List<Product>> streamOfList = orders
.stream()
.map(Order::getProducts);
Stream<Product> streamOfProducts = orders
.stream()
.flatMap(order -> order.getProducts().stream());
IntStream
DoubleStream
LongStream
Order order = repository.load(1L);
if (order != null) {
// Do something with person
}
Optional<Order> optional = repository.load(1L);
optional.ifPresent(order -> {
// Do something with person
});
Order order = repository.load(1L);
if (order == null) {
order = new Order();
}
Optional<Order> optional = repository.load(1L);
Order order = optional.getOrElse(new Order());