diff --git a/src/main/java/fr/univamu/progav/td8/ExercicesStream.java b/src/main/java/fr/univamu/progav/td8/ExercicesStream.java
new file mode 100644
index 0000000000000000000000000000000000000000..e0c1c94f787bbf3798f2ba1824d1cee5dc34d12b
--- /dev/null
+++ b/src/main/java/fr/univamu/progav/td8/ExercicesStream.java
@@ -0,0 +1,123 @@
+package fr.univamu.progav.td8;
+
+import fr.univamu.progav.td1.ExercicesBoucles;
+
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class ExercicesStream {
+
+  public interface Person {
+    int age();
+    String name();
+    boolean isFemale();
+    Person mother();
+    Person father();
+    List<Person> children();
+  }
+
+  // Exercice : compléter toutes les méthodes suivantes
+
+
+  // retourne vrai si la stream contient une femme
+  public static boolean hasFemale(Stream<Person> persons) {
+    // TODO
+    return false;
+  }
+
+
+  // retourne vrai si la stream contient un individu homme et mineur (moins de 18 ans)
+  public static boolean containsMinorMale(Stream<Person> persons) {
+    // TODO
+    return false;
+  }
+
+
+  // retourne un individu de la stream, femme et ayant au moins un enfant,
+  // null si aucun individu ne convient
+  public static Optional<Person> findMother(Stream<Person> persons) {
+    // TODO
+    return Optional.empty();
+  }
+
+
+  // retourne vrai si tous les individus de la stream sont majeurs (âge >= 18)
+  public static boolean areAllMajor(Stream<Person> persons) {
+    // TODO
+    return false;
+  }
+
+
+  // retourne la liste de tous les hommes de la stream.
+  public static List<Person> males(Stream<Person> persons) {
+    // TODO
+    return List.of();
+  }
+
+
+  // retourne le nombre de femmes de la stream
+  public static long nbFemales(Stream<Person> persons) {
+    // TODO
+    return 0L;
+  }
+
+  // trouve un individu de la stream ayant un enfant, et retourne l'âge
+  // de cet enfant.
+  public static Optional<Integer> ageOfSomeChild(Stream<Person> persons) {
+    // TODO
+    return Optional.empty();
+  }
+
+
+  // retourne la personne la plus agée de la stream,
+  public static Optional<Person> eldest(Stream<Person> persons) {
+    // TODO
+    return Optional.empty();
+  }
+
+
+  // retourne une chaîne de caractères, comprenant les noms de toutes les
+  // personnes de la stream, dans l'ordre, séparés par une espace.
+  public static String names(Stream<Person> persons) {
+    // TODO
+    return "";
+  }
+
+
+  // retourne l'âge moyen des individus de la stream
+  public static OptionalDouble averageAge(Stream<Person> persons) {
+    // TODO
+    return OptionalDouble.empty();
+  }
+
+
+  private static class SexCount {
+    // TODO ajouter des propriétés pour compter le nombre d'hommes et de femmes
+    SexCount() {}
+
+    void add(Person p) {
+      // TODO dénombrer p dans les statistiques
+    }
+
+    void merge(SexCount count) {
+      // TODO fusionner les statistiques
+    }
+
+    boolean areEquals() {
+      // TODO renvoyer vrai si les nombres de femmes et d'hommes sont égaux
+      return false;
+    }
+  }
+  // retourne vrai si les nombres d'hommes et de femmes sont égaux.
+  public static boolean hasAsManyMalesAsFemales(Stream<Person> persons) {
+    Supplier<SexCount> supplier = null; // TODO initialiser les 3 variables.
+    BiConsumer<SexCount, Person> accumulator = null;
+    BiConsumer<SexCount,SexCount> combiner = null;
+    return persons
+      .collect(supplier,accumulator,combiner)
+      .areEquals();
+  }
+}
diff --git a/src/main/java/fr/univamu/progav/td8/TD8.md b/src/main/java/fr/univamu/progav/td8/TD8.md
new file mode 100644
index 0000000000000000000000000000000000000000..fa5298272c4400ada22cf2cb23f027a96ee3decd
--- /dev/null
+++ b/src/main/java/fr/univamu/progav/td8/TD8.md
@@ -0,0 +1,33 @@
+Exercice
+========
+
+Compléter les méthodes de la classe `ExercicesStream`.
+
+Vous devez utiliser des Stream. Il est interdit d'utiliser des boucles ou 
+des récursions. Il est interdit de convertir les streams en listes (sauf 
+pour calculer la liste à retourner dans la méthode ```males```). Toutes 
+les méthodes doivent avoir une seule instruction, l'instruction ```return```.
+
+La dernière méthode est la plus difficile, elle demande de construire un 
+collecteur à partir de trois éléments :
+- un ```Supplier<R>```, ```R``` étant le type des objets effectuant la 
+  collection, disons un collecteur. Ce ```Supplier``` permet de créer un 
+  nouveau collecteur; 
+- un ```BiConsumer<R,People>```, qui est une fonction prenant un collecteur 
+  et une personne collectée. C'est en général la méthode du collecteur 
+  chargé d'accepter les personnes collectées l'une après l'autre. Le 
+  collecteur met à jour son état interne pour tenir compte de la personne 
+  collectée.
+- un ```BiConsumer<R,R>```, qui est une fonction permettant de fusionner le 
+  travail de deux collecteurs. Le premier collecteur agrège les données 
+  récoltées par le deuxième collecteur dans son état interne.
+
+Pour nous, le collecteur est un objet calculant le nombre de personnes de 
+chaque sexe. Le premier argument sera donc son constructeur, le deuxième 
+sera sa méthode ```add```, et le troisième sa méthode ```merge```, chacun 
+pouvant être donné par une référence de méthode. Une fois le collecteur 
+effectué, il reste à utiliser la méthode ```areEquals()``` pour déterminer 
+si le nombre de femmes est égal au nombre d'hommes.
+
+Tous les tests de ```ExercicesStreamTest``` doivent passer à la fin de 
+l'exercice.
\ No newline at end of file
diff --git a/src/test/java/fr/univamu/progav/td8/ExercicesStreamTest.java b/src/test/java/fr/univamu/progav/td8/ExercicesStreamTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3c3a1643c87cb50df8cb2e5e66e54b81ccd3cf35
--- /dev/null
+++ b/src/test/java/fr/univamu/progav/td8/ExercicesStreamTest.java
@@ -0,0 +1,169 @@
+package fr.univamu.progav.td8;
+
+import fr.univamu.progav.td8.ExercicesStream;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.OptionalDouble;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static fr.univamu.progav.td8.ExercicesStream.Person;
+import static org.junit.jupiter.api.Assertions.*;
+
+class ExercicesStreamTest {
+
+  private record MockPerson(
+    int age,
+    String name,
+    boolean isFemale,
+    Person mother,
+    Person father,
+    List<Person> children
+  ) implements Person {
+    @Override
+    public String toString() {
+      return this.name;
+    }
+  }
+
+  private static final Person alfa =
+    new MockPerson(78,"Alfa",true,null,null, new ArrayList<>());
+  private static final Person bravo =
+    new MockPerson(82,"Bravo",false,null,null, new ArrayList<>());
+  private static final Person charlie =
+    new MockPerson(42,"Charlie",false, alfa,bravo,List.of());
+  private static final Person delta =
+    new MockPerson(38,"Delta",false, alfa,bravo, new ArrayList<>());
+  private static final Person echo =
+    new MockPerson(41,"Echo",true,null,null, new ArrayList<>());
+  private static final Person foxtrot =
+    new MockPerson(7,"Foxtrot",true,echo,delta, List.of());
+  private static final Person golf =
+    new MockPerson(25,"Golf",true,null,null, new ArrayList<>());
+  private static final Person hotel =
+    new MockPerson(2,"Hotel",false,golf,null, List.of());
+  private static final Person india =
+    new MockPerson(8,"India",true,null,null, List.of());
+  private static final Person juliett =
+    new MockPerson(18,"Juliett",false,null,null,List.of());
+
+  static {
+    alfa.children().addAll(List.of(charlie, delta));
+    bravo.children().addAll(List.of(charlie, delta));
+    delta.children().add(foxtrot);
+    echo.children().add(foxtrot);
+    golf.children().add(hotel);
+  }
+
+  private static final List<Person> ALL =
+    List.of(alfa,bravo,charlie,delta,echo,foxtrot,golf,hotel,india,juliett);
+  private static Stream<Person> femaleStream() {
+    return Stream.of(alfa, echo, foxtrot, golf, india);
+  }
+  private static Stream<Person> maleStream() {
+    return Stream.of(bravo,charlie,delta,hotel,juliett);
+  }
+  @Test
+  void hasFemale() {
+    assertTrue(ExercicesStream.hasFemale(ALL.stream()));
+    assertFalse(ExercicesStream.hasFemale(Stream.of(bravo,charlie,delta)));
+    assertTrue(ExercicesStream.hasFemale(Stream.of(alfa)));
+    assertFalse(ExercicesStream.hasFemale(Stream.of()), "Cas de la stream vide");
+  }
+
+  @Test
+  void containsMinorMale() {
+    assertTrue(ExercicesStream.containsMinorMale(ALL.stream()));
+    assertFalse(ExercicesStream.containsMinorMale(Stream.of(bravo,charlie,delta)), "Pas de mineurs");
+    assertFalse(ExercicesStream.containsMinorMale(Stream.of(foxtrot,india)), "Pas d'hommes");
+    assertFalse(ExercicesStream.containsMinorMale(Stream.of()), "Cas de la stream vide");
+  }
+
+  @Test
+  void findMother() {
+    Optional<Person> found = ExercicesStream.findMother(ALL.stream());
+    assertTrue(found.isPresent());
+    assertTrue(found.get().isFemale() && !found.get().children().isEmpty());
+    assertEquals(Optional.of(golf),ExercicesStream.findMother(Stream.of(bravo,charlie,delta,golf,hotel,india)));
+    assertEquals(Optional.empty(), ExercicesStream.findMother(Stream.of(bravo,charlie,delta,foxtrot,hotel,india)));
+    assertEquals(Optional.empty(), ExercicesStream.findMother(Stream.of()), "Cas de la stream vide");
+  }
+
+  @Test
+  void areAllMajor() {
+    assertFalse(ExercicesStream.areAllMajor(ALL.stream()));
+    assertTrue(ExercicesStream.areAllMajor(Stream.of(alfa,bravo,charlie,delta,echo,golf)));
+    assertTrue(ExercicesStream.areAllMajor(Stream.of(alfa,bravo,charlie,delta,echo,golf,juliett)), "Cas d'un individu ayant 18 ans");
+    assertFalse(ExercicesStream.areAllMajor(Stream.of(foxtrot,hotel,india)));
+    assertTrue(ExercicesStream.areAllMajor(Stream.of()), "Cas de la stream vide");
+  }
+
+  @Test
+  void ageOfSomeChild() {
+    Optional<Integer> maybeAge =
+      ExercicesStream.ageOfSomeChild(Stream.of(hotel, india, juliett, alfa, foxtrot, charlie));
+    assertTrue(maybeAge.isPresent());
+    assertTrue(List.of(charlie.age(),delta.age()).contains(maybeAge.get()));
+    assertTrue(ExercicesStream.ageOfSomeChild(Stream.of(charlie,foxtrot,hotel,india,juliett)).isEmpty());
+    assertTrue(ExercicesStream.ageOfSomeChild(Stream.of()).isEmpty(),"Cas de la stream vide");
+  }
+
+  @Test
+  void males() {
+    List<Person> allMales = ExercicesStream.males(ALL.stream());
+    List<Person> expected = ALL.stream().filter(p -> !p.isFemale()).toList();
+    assertNotNull(allMales);
+    assertEquals(expected.size(), allMales.size(), "Nombre d'hommes incorrect");
+    for (Person p : expected ) {
+      assertTrue(allMales.contains(p), p.name() + " est un homme qui n'est pas dans la stream");
+    }
+    List<Person> neitherMaleNorFemale = ExercicesStream.males(femaleStream());
+    assertNotNull(neitherMaleNorFemale);
+    assertTrue(neitherMaleNorFemale.isEmpty());
+    List<Person> empty = ExercicesStream.males(Stream.of());
+    assertNotNull(empty, "Cas de la stream vide");
+    assertTrue(empty.isEmpty(), "Cas de la stream vide");
+  }
+
+  @Test
+  void nbFemales() {
+    assertEquals(0,ExercicesStream.nbFemales(Stream.of()), "Cas de la stream vide");
+    assertEquals(5,ExercicesStream.nbFemales(ALL.stream()));
+    assertEquals(0,ExercicesStream.nbFemales(maleStream()));
+  }
+
+  @Test
+  void eldest() {
+    assertTrue(ExercicesStream.eldest(Stream.of()).isEmpty(), "Cas de la stream vide");
+    assertEquals(Optional.of(bravo),ExercicesStream.eldest(ALL.stream()));
+  }
+
+  @Test
+  void names() {
+    String allNames = ExercicesStream.names(ALL.stream()).trim();
+    assertEquals("Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett",allNames);
+    assertEquals("",ExercicesStream.names(Stream.of()), "Cas de la liste vide");
+  }
+
+  @Test
+  void hasAsManyMalesAsFemales() {
+    assertTrue(ExercicesStream.hasAsManyMalesAsFemales(Stream.of()), "Cas de la liste vide");
+    assertFalse(ExercicesStream.hasAsManyMalesAsFemales(Stream.of(alfa)));
+    assertFalse(ExercicesStream.hasAsManyMalesAsFemales(Stream.of(bravo)));
+    assertFalse(ExercicesStream.hasAsManyMalesAsFemales(femaleStream()));
+    assertFalse(ExercicesStream.hasAsManyMalesAsFemales(maleStream()));
+    assertTrue(ExercicesStream.hasAsManyMalesAsFemales(ALL.stream()));
+  }
+
+  @Test
+  void averageAge() {
+    assertTrue(ExercicesStream.averageAge(Stream.of()).isEmpty(), "Cas de la liste vide");
+    OptionalDouble average = ExercicesStream.averageAge(ALL.stream());
+    assertTrue(average.isPresent());
+    double expected = 34.1;
+    assertTrue(Math.abs(average.getAsDouble() - expected) < 0.1, "La moyenne correcte est " + expected);
+  }
+}
\ No newline at end of file