:: Enseignements :: Master :: M1 :: 2021-2022 :: Java Avancé ::
[LOGO]

Liste, table de hachage, entrées/sorties, stream, lambdas


Exercice 1 - Path, Stream et try-with-resources

On cherche à écrire un code correct permettant d'afficher le nombre de lignes d'un fichier. Vous écrirez le code dans une classe Main d'un package nommé fr.umlv.movie.

  1. Avant de commencer, rappeler pourquoi, depuis la version 7 de Java, on représente un chemin en Java en utilisant la classe java.nio.file.Path et pas la classe java.io.File comme précédemment.
  2. Créer un Path en utilisant la factory method Path.of() sur le fichier movies.txt.
  3. En utilisant la méthode Files.lines qui permet d'extraire toutes les lignes d'un fichier, afficher leurs nombre à l'aide de la méthode count. Dans un premier temps, nous allons essayer de gérer l'exception éventuelle à l'aide d'un try-catch.
  4. En fait, le code que vous avez écrit est (très probablement) faux, car vous avez ouvert un fichier, vu celui-ci comme un Stream mais vous avez oublié de fermer le Stream pour fermer le fichier sous-jacent avec la méthode close.
    Modifiez votre code.
  5. Le code est encore (très probablement) faux, car si vous avez une exception lors du count(), la méthode close ne sera jamais appelée.
    Modifiez votre code en utilisant un finally pour résoudre le problème.
  6. Si vous vous êtes laissés guider par Eclipse, vous vous êtes probablement donné beacoup de mal pour réussir à fermer un stream qui n'a même pas été ouvert...
    Modifiez votre code pour ne pas avoir à initialiser le stream avec null.
  7. Quelle est la différence entre l'utilisation de la construction try-catch et un throws.
    Pourquoi vaudrait-il mieux utiliser un throws ici?
    Modifier le code en conséquence.
  8. En fait, il est plus pratique d'utiliser la construction try-with-resources, le try(...) car dans ce cas l'appel à close est fait automatiquement à la fin du bloc try.
    Modifiez une nouvelle fois votre code.
  9. Pourquoi le try(..) est mieux que le try/finally ?
    Notez de plus que les deux premiers morceaux de code que vous avez écrits semblaient bons et marchaient alors qu'ils étaient farcis de bugs. C'est le gros problème des tests : ce n'est pas parce qu'un test affiche ce qu'il faut que le code est correct.

Exercice 2 - Movie Stars

Le but de cet exercice est de manipuler une liste de films, extraits du fichier movies.txt pour compter les acteurs de films.
Le fichier contient par ligne, le nom du film et les noms de ses acteurs, le tout séparés par des points-virgules.

Les tests JUnit 5 de cet exercice sont MoviesTest.java.

  1. Comme dans les questions suivantes, on va manipuler des films, créer un record Movie qui représente un film avec un titre (title) et une liste d'acteurs (actors).
    Attention à faire en sorte que Movie soit non mutable (donc attention à la liste d'acteurs).
  2. On cherche maintenant à implanter une méthode movies dans la classe fr.umlv.movie.Movies qui lit un fichier (désigné par un Path) et renvoie une liste de Movie suivant le format décrit plus haut.
    Pour cela, écrivez le code qui lit le fichier ligne à ligne avec la méthode Files.lines et qui stocke les films dans une List.
    Pour séparer une chaîne de caractères en plusieurs parties, il existe la méthode String.split(). Il existe également une méthode Stream.skip() qui permet de ne pas considérer certaines valeurs dans un Stream.
    Si ce n'est pas déjà fait, faite en sorte que la liste retournée soit non modifiable en utilisant la méthode Stream.toList().
  3. Si l'on veut rechercher un film, utiliser une java.util.List n'est pas la bonne structure de données, on préfère utiliser une java.util.Map.
    Écrire une fonction movieMap qui prend en paramètre une liste de films et renvoie une Map non modifiable qui associe au nom d'un film l'objet Movie correspondant.
    Note : quel Collector doit-on utiliser ?
  4. Il existe une méthode Function.identity(). Comment peut-on l'utiliser dans notre cas ?
  5. On cherche à afficher le nombre total d'acteurs ayant joué dans au moins un film.
    L'idée est de créer un Stream d'acteurs à partir de la liste de films, puis de les compter.
    À quoi sert la méthode Stream.flatMap() ?
    Comment peut-on l'utiliser dans notre cas ?
    Pour tester, affichez les 20 premiers acteurs à partir du Stream de tous les acteurs. Utilisez limit pour les 20 premiers et forEach pour l'affichage.
  6. Au lieu d'afficher les 20 premiers, comptez le nombre d'acteurs et affichez le résultat.
  7. En fait, le calcul précédent est faux car nous pouvons compter le même acteur plusieurs fois, il faut éviter les doublons !
    Dans un premier temps, nous allons éviter les doublons en stockant les acteurs dans une structure de données qui a la propriété de ne pas enregistrer les doublons.
    Quelle est l'interface Java qui correspond à cette structure de données ?
    Quelle implantation de l'interface allons-nous choisir ?
    Écrire le code de la méthode numberOfUniqueActors qui prend en paramètre une liste de films et renvoie le nombre total d'acteurs différents ayant joué dans les films, sous forme d'un entier long.
  8. En fait, il existe une méthode Stream.distinct(). Comment peut-on l'utiliser pour trouver le nombre total d'acteurs?
    Écrire le code correspondant.
  9. On souhaite maintenant écrire une méthode numberOfMoviesByActor qui prend paramètre une liste de films et calcule pour chaque acteur le nombre de films auxquels il a participé.
    Quel est le type de retour de la méthode numberOfMoviesByActor ?
    Pour l’implantation, on va repart de notre Stream d'acteurs (non uniques) et on compte le nombre d'apparitions de chaque acteur.
    Nous allons pour cela utiliser un Collector particulier appelé Collectors.groupingBy(Function).
    Rappeler comment marche la méthode collect et les Collector.
    Comment peut-on utiliser le collecteur ci-dessus pour grouper les acteurs en fonction d'eux-mêmes ?
    Quelle sera le type de retour de l'appel à collect ?
    Écrire le code de la méthode numberOfMoviesByActor puis dans le main afficher le nombre de films auxquels a participé Brad Pitt (ou un autre acteur de votre choix), histoire de voir quelque chose !
  10. On cherche enfin à écrire une méthode actorInMostMovies qui prend en paramètre la structure de donnée qui associe à un acteur le nombre de film dans lequel il a joué (le résultat de la fonction de la question précédente), et renvoie une paire contenant l'acteur ayant joué dans le plus de film ainsi que le nombre de films dans lequel il a joué.
    Il n'existe pas de classe Pair en Java, car on peut utiliser un record. Définir, dans un premier temps, le record ActorMovieCount qui contient un acteur (actor) ainsi que le nombre de films (movieCount) dans lequel il a joué.
    Sachant que la structure de donnée passée en paramètre pourrait être vide, quel doit être le type de retour de cette méthode ?
    En fait, il s'agit de trouver le maximum parmi tous les couples ActorMovieCount, et l'on peut utiliser pour cela le collecteur Collectors.maxBy(). sur le stream des couples ActorMovieCount de la structure de donnée passée en paramètre.
    Attention, le comparateur attendu par maxBy ne doit pas être capable de comparer des nombre de films mais des couples ActorMovieCount.