:: Enseignements :: Master :: M1 :: 2016-2017 :: Java Avancé ::
[LOGO]

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


Eclipse s'exécute en tapant dans un terminal :
eclipse-ligth
Une fois démarré, il faut le configurer pour qu'il utilise le bon JDK, donc dans Window > Preferences > Java > Installed JREs, cliquer sur Add, choisir "Standard VM" puis entrer le chemin /usr/local/apps/java9, puis cliquer sur "Finish". Enfin, selectionner le JDK que vous venez de créer comme JRE par défaut.
Eclipse va maintenant utiliser le JDK 9, il reste à lui dire d'utiliser le compilateur 1.9, dans Window > Preferences > Java > Compiler, le Compiler compliance level doit être à 1.9 (beta)
ouf, c'est fait !

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

On cherche à écrire un code correct permettant d'afficher les lignes d'un fichier.

  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 Paths.get() sur le fichier movies.txt.
  3. 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, nous allons essager de gérer l'exception eventuelle à 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 forEach, 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 à initialser 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. A la maison, faîtes 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.

  1. Dans un premier temps, écrivez un code qui lit le fichier ligne à ligne avec la méthode Files.lines et qui stocke dans une table de hachage la liste d'acteurs correspondante (dans une Map<String, List<String>> donc).
    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.toMap et utilisez-la dans votre code.
  2. On cherche maintenant à afficher le nombre total d'acteurs ayant joué dans au moins un film.
    L'astuce consiste à ne pas se préoccuper des films mais uniquement des acteurs et donc, dans un premier temps, de créer un Stream d'acteurs.
    A 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.
  3. Au lieu d'afficher les 50 premiers, comptez le nombre d'acteurs et affichez le résultat.
  4. 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 permttant d'afficher le nombres d'acteurs différents ayant joué dans les films de la liste.
  5. 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.
  6. On souhaite maintenant calculer le nombre de films auxquels a participé chaque acteur.
    Nous allons tricher, on va repartir de notre Stream d'acteur (non unique) et compter le nombre d'apparition 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 puis afficher le résultat pour Brad Pitt (ou un autre acteur de votre choix), histoire de voir quelque chose !
  7. Il existe une méthode Function.identity(). Comment peut-on l'utiliser dans notre cas ?

Exercice 3 - Scoobi Dooooo (optionnel)

On aimerait ne pas avoir une liste d'acteurs comme valeur de la Map résultante mais plutôt directement le nombre de fois qu'un acteur apparaît dans la liste.

  1. Il est possible en Java de compter le nombre de fois que l'on voit un acteur pendant la création de la Map (lors de la collection). On utilise pour cela une surcharge de la méthode Collectors.html.groupingBy(Function, Collector) qui prend en second paramètre un nouveau collecteur qui agira sur les valeurs de la Map.
    Quelle collecteur doit-on utiliser comme second collecteur (downstream collector en anglais) ?
    Utilisez celui-ci pour calculer le nombre de films pour chaque acteur, dans une Map<String, Long>. Vous pouvez afficher l'acteur de votre choix et aussi vérifier que la taille de la Map correspond bien au nombre d'acteurs uniques trouvés précédemment.
    Indication: les Collector par défaut sont déclarés dans la classe Collectors !
  2. Il reste enfin à trouver l'acteur (le couple nom d'acteur/nombre de films) qui joue dans le plus de films.
    Pour cela, il faut voir la Map comme un ensemble de couples clé/valeur (en utilisant entrySet) puis trouver le couple ayant le plus grand nombre de films grâce à la méthode Collectors.maxBy().
    Écrire le code correspondant, trouver l'acteur ayant joué dans le plus de films et vérifier avec Wikipédia que cela à un rapport avec le titre de l'exercice.