:: Enseignements :: Master :: M1 :: 2019-2020 :: Java Avancé ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) | Structure de données persistante (fonctionnelle) |
Le but de ce TP est de bien faire la différence entre les informations exposées publiquement par une classe
et les informations utilisées par celle-ci en interne.
Nous allons implanter une classe
fr.umlv.seq.Seq qui représente une séquence
(comme une liste) paresseuse non mutable d'éléments non
null.
Les élément sont stockés en interne dans une liste non mutable.
Exercice 1 - Seq
La classe
fr.umlv.seq.Seq est paramétrée par le type des éléments qu'elle contient.
La classe
Seq est munie des opérations initiales suivantes:
-
from qui prend une liste en paramètre et créé un objet Seq contenant les éléments de la liste (dans le même ordre);
-
get qui permet d'obtenir le n-ième élément d'un Seq (avec une comnplexité en O(1), bien sûr);
-
size qui indique le nombre d'éléments dans la séquence (en O(1), toujours).
Note: comme la séquence est non mutable, le nombre d'éléments de la séquence ne change pas.
Exemple de création et d'utilisation d'un
Seq
var seq = Seq.from(List.of(78, 56, 34, 23));
System.out.println(seq.size()); // 4
System.out.println(seq.get(2)); // 34
Les tests unitaires JUnit 5 correspondant à l'implantation sont ici:
SeqTest.java.
-
Écrire le code de la classe Seq dans le package fr.umlv.seq.
-
Écrire une méthode d'affichage permettant d'afficher les valeurs d'un Seq séparées par des virgules (suivies d'un espace), l'ensemble des valeurs
étant encadré par des chevrons ('<' et '>').
Par exemple, avec le Seq créé précédemment
System.out.println(seq); // <78, 56, 34, 23>
-
Écrire une méthode of permettant d'initialiser un Seq à partir de valeurs séparées par des virgules.
Par exemple, on pourra créer le Seq précédent comme ceci
var seq = Seq.of(78, 56, 34, 23);
Note: si vous avez des warnings, vous avez un problème.
Note 2: si vous pensez un @SuppressWarnings, pensez plus fort !
-
Écrire une méthode forEach qui prend en paramètre une fonction qui prend en paramètre chaque élément un par un et fait un effet de bord.
Par exemple, on pourra afficher les éléments un par un de la façon suivante
seq.forEach(element -> System.out.println(element));
-
On souhaite écrire une méthode map qui prend en paramètre une fonction à appliquer à chaque élément d'un Seq pour créer un nouveau Seq.
On souhaite avoir une implantation paresseuse, c'est à dire une implantation qui ne fait pas de calcul si ce n'est pas nécessaire.
Par exemple, tant que personne n'accède à un élément du nouveau Seq, il n'est pas nécessaire d'appliquer la fonction. L'idée est de stoker les anciens éléments ainsi que la fonction et de l'appliquer seulement si c'est nécessaire.
Bien sûr, cela va nous obliger à changer l'implantation déjà existante de Seq car maintenant tous les Seq
vont stocker une liste d'éléments ainsi qu'une fonction de transformation (de mapping).
Exemple d'utilisation
var seq2 = seq.map(String::valueOf); // String.valueOf() est pas appelée
System.out.println(seq2.get(0)); // "78", String.valueOf a été appelée 1 fois
// car on demande explicitement la valeur
Avant de se lancer dans l'implantation de map, quelle doit être sa signature ?
Quel doit être le type des éléments de la liste ? Et le type de la fonction stockée ?
Faire les modifications correspondantes, puis changer le code des méthodes pour les prendre en compte. Enfin, écrire le code de map.
Note : le code doit fonctionner si l'on appelle map deux fois successivement.
-
Écrire une méthode findFirst qui renvoie le premier élément du Seq si celui-ci existe.
-
Faire en sorte que l'on puisse utiliser la boucle for-each-in sur un Seq
Par exemple,
for(var value: seq) {
System.out.println(value);
}
-
Enfin, on souhaite implanter la méthode stream() qui renvoie un Stream des éléments du Seq.
Pour cela, on va commencer par implanter un Spliterator
que l'on peut construire à partir du Spliterator déjà existant de la liste
(que l'on obtient avec la méthode List.spliterator()).
Puis en utilisant la méthode StreamSupport.stream, créer un Stream à partir de ce Spliterator.
Écrire la méthode stream().
Exercice 2 - Seq2 le retour (à la maison)
En fait, l'implantation à base de liste de l'exercice précédent n'est pas très efficace en mémoire.
Il vaux mieux stocker les éléments dans un tableau car c'est de toute façon le stockage
utilisé par la liste (pas complètement vrai si vous utilisez List.of(), on vous laisse trouver pourquoi).
Les tests unitaires JUnit 5 correspondant à l'implantation sont ici:
Seq2Test.java.
-
Ré-implanter toutes les méthodes publiques de Seq dans une classe Seq2 en utilisant un tableau d'éléments en interne.
© Université de Marne-la-Vallée