Atelier 15 - Bibliothèque musicale
Créez un programme Java qui gère une bibliothèque de chansons en utilisant les Streams, les listes immuables et les streams primitifs.
Spécifications
1. Enum Genre
Créez un enum Genre avec les valeurs : POP, ROCK, JAZZ, CLASSIQUE, HIP_HOP, ELECTRO.
2. Classe Chanson
Créez une classe Chanson avec les attributs suivants :
titre(String)artiste(String)genre(Genre)duree(int) — durée en secondesannee(int)note(double) — entre 0.0 et 5.0
Redéfinissez toString() pour afficher :
[GENRE] Titre — Artiste (durée s, annee) ★note
3. Bibliothèque immuable
Créez la liste initiale avec List.of() — au moins 12 chansons couvrant différents genres, durées et notes.
Cette liste est immuable. Si vous avez besoin d'une copie modifiable, encapsulez-la dans
new ArrayList<>(...).
4. Opérations intermédiaires
4a. filter — Filtrage
Créez et appliquez les filtres suivants (chacun suivi d'un collect puis d'un affichage) :
| Filtre | Condition |
|---|---|
| Chansons récentes | annee >= 2020 |
| Chansons bien notées | note >= 4.0 |
| Chansons courtes | duree < 180 (moins de 3 minutes) |
| Chansons ROCK | genre == Genre.ROCK |
4b. map — Transformation
- Extrayez la liste des titres en majuscules de toutes les chansons
- Transformez chaque chanson en une chaîne formatée :
"Titre — Artiste" - Extrayez uniquement les noms d'artistes (toute la bibliothèque)
4c. sorted — Tri
Appliquez les tris suivants (sur une copie modifiable de la liste) :
- Par note décroissante (
Comparator.comparingDouble) - Par durée croissante
- Par titre alphabétique (référence de méthode)
4d. distinct, limit, skip
- Extrayez la liste des genres distincts présents dans la bibliothèque
- Affichez les 5 premières chansons de la liste triée par note décroissante (top 5)
- Affichez les chansons en ignorant les 3 premières (pagination — page 2 avec
skip)
5. Opérations terminales
5a. count
Comptez :
- Le nombre total de chansons
- Le nombre de chansons avec une note supérieure à 4.0
- Le nombre de chansons de genre JAZZ
5b. anyMatch, allMatch, noneMatch
Vérifiez les conditions suivantes et affichez le résultat (true/false) :
| Question | Méthode |
|---|---|
| Y a-t-il au moins une chanson CLASSIQUE ? | anyMatch |
| Toutes les chansons ont-elles une note > 1.0 ? | allMatch |
| Aucune chanson n'a-t-elle une durée de 0 seconde ? | noneMatch |
5c. findFirst
Trouvez la première chanson de genre HIP_HOP dans la bibliothèque. Si aucune n'existe, affichez "Aucune chanson HIP_HOP trouvée" via orElse.
5d. reduce
- Calculez la durée totale de toutes les chansons avec
reduce - Trouvez la note maximale avec
reduceetDouble::max
6. collect — Collecte avancée
- Joindre les titres des chansons bien notées (note ≥ 4.0) en une seule chaîne séparée par
" | "avecCollectors.joining - Regrouper par genre avec
Collectors.groupingBy— obtenez uneMap<Genre, List<Chanson>>et affichez le nombre de chansons par genre - Collecter dans un Set les artistes distincts (sans doublons) avec
Collectors.toSet
7. Streams primitifs
7a. mapToInt — Statistiques de durée
À partir de la bibliothèque, utilisez mapToInt pour calculer :
- La durée totale de toutes les chansons (en secondes)
- La durée moyenne (
average()) — gérez le cas d'un stream vide avecorElse - La durée maximale et la durée minimale
Affichez la durée totale convertie en minutes et secondes :
Durée totale : 52 min 14 s
Durée moyenne : 3 min 41 s
7b. IntStream.range et rangeClosed
- Utilisez
IntStream.rangeClosed(2015, 2024)pour afficher combien de chansons ont été publiées chaque année présente dans la bibliothèque - Utilisez
IntStream.range(0, 5)pour afficher les indices et titres du top 5 (chansons triées par note décroissante)
7c. boxed() — Conversion vers objets
Générez la liste des durées de toutes les chansons sous forme de List<Integer> en utilisant mapToInt(...).boxed().collect(...).
8. Pipeline combiné
Écrivez un seul pipeline (sans variables intermédiaires) qui :
- Filtre les chansons avec
note >= 3.5 - Filtre les chansons publiées après 2018
- Trie par note décroissante
- Prend les 3 premières
- Transforme en chaîne
"Titre (Artiste)" - Collecte dans une
List<String>
Affichez le résultat.
9. Débogage avec peek
Reprenez le pipeline de la section 8 et ajoutez des peek() entre chaque étape pour observer le flux des éléments :
[filter note] [HIP_HOP] Titre...
[filter annee] [HIP_HOP] Titre...
[sorted] [ROCK] Titre...
...
Rappel : retirez les
peek()une fois le débogage terminé — ils ne doivent pas rester en production.
Exemple d'exécution attendu
=== Bibliothèque complète ===
[POP] Blinding Lights — The Weeknd (200 s, 2019) ★4.8
[ROCK] Bohemian Rhapsody — Queen (354 s, 1975) ★5.0
[JAZZ] So What — Miles Davis (562 s, 1959) ★4.6
...
=== Chansons récentes (>= 2020) ===
[ELECTRO] Levitating — Dua Lipa (204 s, 2020) ★4.2
...
=== Top 5 par note ===
[ROCK] Bohemian Rhapsody — Queen (354 s, 1975) ★5.0
[POP] Blinding Lights — The Weeknd (200 s, 2019) ★4.8
...
=== Statistiques de durée ===
Durée totale : 52 min 14 s
Durée moyenne : 3 min 41 s
Durée max : 9 min 22 s
Durée min : 2 min 15 s
=== Genres distincts ===
[POP, ROCK, JAZZ, CLASSIQUE, HIP_HOP, ELECTRO]
=== Chansons par genre ===
POP : 3 chanson(s)
ROCK : 2 chanson(s)
JAZZ : 2 chanson(s)
...
=== Y a-t-il une chanson CLASSIQUE ? true ===
=== Toutes les notes > 1.0 ? true ===
=== Aucune durée à 0 ? true ===
=== Première chanson HIP_HOP ===
[HIP_HOP] HUMBLE. — Kendrick Lamar (177 s, 2017) ★4.5
=== Durée totale (reduce) : 3134 s ===
=== Note maximale (reduce) : 5.0 ===
=== Top 3 récent et bien noté ===
Bohemian Rhapsody (Queen)
Blinding Lights (The Weeknd)
So What (Miles Davis)
Contraintes
- La liste initiale doit être créée avec
List.of()— ne pas utilisernew ArrayList<>()directement - Toutes les transformations doivent utiliser des Streams — pas de boucles
forouwhile - Les statistiques de durée de la section 7a doivent utiliser
mapToInt(pasmap+reduce) reduce(section 5d) doit être utilisé — pasmapToInt(...).sum()pour cette sectionCollectors.groupingByest obligatoire pour la section 6.2peek()est utilisé uniquement pour le débogage — il doit être retiré (ou commenté) dans le code final- Les
Optional(average(),max(),min(),findFirst()) doivent être traités — pas degetAsDouble()sans vérification préalable
Questions de réflexion
- Quelle est la différence entre
List.of()etnew ArrayList<>()? Quand utiliser l'un plutôt que l'autre ? - Pourquoi un Stream ne peut-il être consommé qu'une seule fois ? Que se passe-t-il si on essaie de l'utiliser deux fois ?
- Quelle est la différence entre
Stream<Integer>etIntStream? Pourquoi préférermapToInt()pour calculer une somme de durées ? - Un pipeline de Stream sans opération terminale produit-il un résultat ? Que se passe-t-il concrètement ?
- Pourquoi
peek()ne doit-il pas être utilisé en production pour déclencher des effets de bord ? - Dans quel cas est-il préférable d'utiliser une boucle
forplutôt qu'un Stream ?