:: Enseignements :: Master :: M1 :: 2019-2020 :: Java Avancé ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) |
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 les lignes d'un fichier. Vous écrirez le code dans un package nommé fr.umlv.movies.
-
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.
-
Créer un Path en utilisant la factory method Path.of() sur le fichier movies.txt.
-
En utilisant la méthode Files.lines
qui permet d'extraire toutes les lignes d'un fichier, afficher celles-ci à l'aide de la méthode forEach. Dans un premier temps, nous allons essayer de gérer l'exception éventuelle à l'aide d'un try-catch.
-
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.
-
Le code est encore (très probablement) faux, car si vous avez une exception lors du forEach, la méthode close ne sera jamais appelée.
Modifiez votre code en utilisant un finally pour résoudre le problème.
-
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.
-
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.
-
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.
-
A la maison, faites une recherche pour savoir pourquoi le try(..) est mieux que le try/finally.
Notez 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 un ensemble 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.
-
On cherche dans un premier temps à écrire une méthode actorsByMovie
qui prend en paramètre un fichier (un Path) et renvoie une Map qui associe
à un nom de film une List des noms des acteurs.
Pour cela,
écrivez le code qui lit le fichier ligne à ligne avec la méthode
Files.lines
et qui stocke les infos dans la Map.
Pour séparer une chaîne de caractères en plusieurs parties, il existe la méthode
String.split().
Il existe de plus une méthode
Stream.skip()
qui permet de ne pas prendre en compte certaines valeurs dans un stream.
Si ce n'est pas déjà fait, allez regarder la documentation de la méthode
Collectors.toUnmodifiableMap et utilisez-la dans votre code.
-
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 Map précédente, puis de les compter.
À quoi sert la méthode
Stream.flatMap() ?
Comment peut-on l'utiliser dans notre cas ?
Pour tester, affichez les 50 premiers acteurs à partir du Stream de tous les acteurs.
Utilisez limit pour les 50 premiers et forEach pour l'affichage.
-
Au lieu d'afficher les 50 premiers, comptez le nombre d'acteurs et affichez le résultat.
-
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 Map qui associe à un film la liste de ses acteurs et renvoie le nombre total d'acteurs différents ayant joué dans les films de la Map, sous forme d'un entier long.
-
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.
-
On souhaite maintenant écrire une méthode numberOfMoviesByActor qui prend paramètre
une Map qui associe à un film la liste de ses acteurs
et calcule pour chaque acteur le nombre de films auxquels il a participé.
Quelle est le type de retrour de la méthode numberOfMoviesByActor ?
Pour l’implantation, nous allons tricher, on va repartir de notre Stream d'acteurs (non uniques) et compter 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 !
-
Il existe une méthode
Function.identity().
Comment peut-on l'utiliser dans notre cas ?
-
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é.
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 Java, il n'existe pas de classe Pair, car il existe l'interface Map.Entry
et sa factory method Map.entry(first, second).
En fait, il s'agit de trouver le maximum parmi tous les couples (acteur, nombre de films),
et l'on peut utiliser pour cela le collecteur
Collectors.maxBy().
sur le stream des couples (acteur, nombre de films) 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 (acteur, nombres de film).
© Université de Marne-la-Vallée