Aller au contenu principal

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 secondes
  • annee (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) :

FiltreCondition
Chansons récentesannee >= 2020
Chansons bien notéesnote >= 4.0
Chansons courtesduree < 180 (moins de 3 minutes)
Chansons ROCKgenre == Genre.ROCK

4b. map — Transformation

  1. Extrayez la liste des titres en majuscules de toutes les chansons
  2. Transformez chaque chanson en une chaîne formatée : "Titre — Artiste"
  3. Extrayez uniquement les noms d'artistes (toute la bibliothèque)

4c. sorted — Tri

Appliquez les tris suivants (sur une copie modifiable de la liste) :

  1. Par note décroissante (Comparator.comparingDouble)
  2. Par durée croissante
  3. Par titre alphabétique (référence de méthode)

4d. distinct, limit, skip

  1. Extrayez la liste des genres distincts présents dans la bibliothèque
  2. Affichez les 5 premières chansons de la liste triée par note décroissante (top 5)
  3. Affichez les chansons en ignorant les 3 premières (pagination — page 2 avec skip)

5. Opérations terminales

5a. count

Comptez :

  1. Le nombre total de chansons
  2. Le nombre de chansons avec une note supérieure à 4.0
  3. Le nombre de chansons de genre JAZZ

5b. anyMatch, allMatch, noneMatch

Vérifiez les conditions suivantes et affichez le résultat (true/false) :

QuestionMé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

  1. Calculez la durée totale de toutes les chansons avec reduce
  2. Trouvez la note maximale avec reduce et Double::max

6. collect — Collecte avancée

  1. Joindre les titres des chansons bien notées (note ≥ 4.0) en une seule chaîne séparée par " | " avec Collectors.joining
  2. Regrouper par genre avec Collectors.groupingBy — obtenez une Map<Genre, List<Chanson>> et affichez le nombre de chansons par genre
  3. 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 :

  1. La durée totale de toutes les chansons (en secondes)
  2. La durée moyenne (average()) — gérez le cas d'un stream vide avec orElse
  3. 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

  1. Utilisez IntStream.rangeClosed(2015, 2024) pour afficher combien de chansons ont été publiées chaque année présente dans la bibliothèque
  2. 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 :

  1. Filtre les chansons avec note >= 3.5
  2. Filtre les chansons publiées après 2018
  3. Trie par note décroissante
  4. Prend les 3 premières
  5. Transforme en chaîne "Titre (Artiste)"
  6. 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 utiliser new ArrayList<>() directement
  • Toutes les transformations doivent utiliser des Streams — pas de boucles for ou while
  • Les statistiques de durée de la section 7a doivent utiliser mapToInt (pas map + reduce)
  • reduce (section 5d) doit être utilisé — pas mapToInt(...).sum() pour cette section
  • Collectors.groupingBy est obligatoire pour la section 6.2
  • peek() 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 de getAsDouble() sans vérification préalable

Questions de réflexion

  • Quelle est la différence entre List.of() et new 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> et IntStream ? Pourquoi préférer mapToInt() 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 for plutôt qu'un Stream ?