:: Enseignements :: Master :: M1 :: 2021-2022 :: Java Avancé ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) | Interface, Vue et implantation de Stream |
Le but de ce TP d'implanter une vue qui permet de voir les données
d'un tableau ou d'une liste de la même façon puis de voir ces
données en sens inverse sans déplacer physiquement les données.
Nous allons implanter une classe
fr.umlv.seq.Reversible qui représente des éléments mutables,
non pas parce que l'API offre des méthodes de mutation mais parce que les données sont issues
d'une structure de données mutable (un tableau ou une
java.util.List).
Exercice 1 - Reversible
L'interface
fr.umlv.reversible.Reversible permet de voir les données d'un tableau ou bien d'une liste
dans l'ordre habituel (de 0 à taille exclue) ou dans l'ordre inverse (de taille - 1 à 0 inclus).
L'interface est une vue, les implantations de cette interface ne stockent pas directement
les données : elles utilisent les données stockées dans le tableau ou la liste passé à leur création
sans faire de copie.
L'interface
Reversible possède (entre autre) les opérations suivantes :
-
size qui renvoie le nombre d'éléments disponibles à la création du Reversible.
-
get(index) qui renvoie l'élément à l'index index stocké
dans le tableau ou la liste sous-jacente.
-
fromArray(elements) qui permet de créer un Reversible à partir
d'un tableau des éléments
-
fromList(list) qui permet de créer un Reversible à partir
d'une liste des éléments.
-
reversed() qui renvoie un Reversible qui permet de voir les éléments
dans l'ordre inverse, le premier élément est le dernier, le second est le deuxième en partant de la
fin, etc.
L'interface Reversible est paramétrée par le type des éléments qu'elle contient et
ne permet pas de faire l'opération si l'élément impliqué est null.
Voici un exemple d'utilisation de l'interface
Reversible.
var reversible = Reversible.fromArray("foo", "bar", "baz", "whizz");
System.out.println(reversible.size()); // 4
System.out.println(reversible.get(1)); // bar
System.out.println(reversible.reversed().get(1)); // baz
Pour la dernière ligne,
baz est bien l'élément 1 à partir de la fin (on commence à 0).
-
On souhaite créer l'interface Reversible avec la méthode fromArray permettant
de créer un Reversible à partir des éléments séparés par des virgules.
L'instance renvoyée doit avoir les méthodes size, get implantées.
La méthode fromArray ne doit pas permettre de créer un Reversible si l'un des éléments
est null. De plus, comme il est possible de changer une des valeurs du tableau après la création
du Reversible, la méthode get lèvera l'exception NullPointerException
si celle-ci détecte que l'élément demandé a été remplacé par null
(voir le test unitaire getAlterAfterCreation).
Écrire la méthode fromArray dans l'interface Reversible
et vérifier que les tests marqués "Q1" passent.
Note : on vous demande de ne pas écrire l'implantation de l'interface Reversible
dans un autre fichier que Reversible.java.
-
On souhaite pouvoir parcourir un Reversible en utilisant une boucle for
comme ceci :
var reversible = Reversible.fromArray(4, 6, 8);
for(var value: reversible) {
...
}
Modifier le code pour pouvoir parcourir un Reversible en utilisant une boucle for
et vérifier que les tests marqués "Q2" passent.
Note : le code peut-être ajouté à deux endroits différents... lequel choisissez-vous ?
-
On souhaite ajouter une méthode reversed qui, à partir du Reversible auquel on l'applique,
renvoie un autre Reversible qui permet de voir les éléments en ordre inverse (on a bien dit "voir", il ne s'agit de fabriquer un nouveau tableau inversé).
Modifier le code pour ajouter la méthode reversed et vérifier que les tests marqués "Q3"
passent. Attention, on ne veut pas que le code de l'itérateur soit dupliqué.
-
Si vous ne l'avez pas vu, on peut remarquer qu'appliquer reversed deux fois de suite
doit renvoyer le Reversible initial.
Modifier votre code pour avoir ce comportement et vérifier que le test marqué Q4 passe.
-
On souhaite ajouter une méthode fromList qui permet de créer un Reversible
à partir d'une liste. Contrairement à un tableau, une liste peut changer de taille dynamiquement
si l'on ajoute ou retire des éléments, il va donc falloir gérer ce problème.
Pour cela, la taille du Reversible sera toujours la taille de la liste à la création
du Reversible. De plus, si la liste possède moins d'éléments que le Reversible,
alors la méthode get lèvera l'exception IllegalStateException.
Si la liste a plus d'éléments que la taille du Reversible, le Reversible ne montrera que
les éléments compris entre 0 et sa taille.
Il faudra aussi lever l'exception ConcurrentModificationException lors d'un parcours,
si le nombre d'éléments de la liste est inférieur à la taille de la liste lors de la création
du Reversible.
Comme pour fromArray, fromList devra rejeter les listes contenant un élément null
et la méthode get devra lever une NullPointerException
si celle-ci détecte que l'élément demandé a été remplacé par null.
Ajouter la méthode fromList et vérifier que les tests marqués "Q5" passent.
Note : essayer de partager le code entre fromArray et fromList, par exemple,
en voyant le tableau comme une List (cela correspond au test fromArrayAndFromListSharedCode).
-
On souhaite ajouter à l'interface Reversible une méthode stream
qui renvoie un Stream des éléments visibles par le Reversible.
Modifier votre code pour implanter la méthode stream et vérifier que les tests
marqués "Q6" passent.
-
Si vous ne l'avez pas déjà fait, on souhaite que le Stream renvoyé par la méthode
stream permette d’effectuer les calculs en parallèle sur les éléments du Reversible.
Modifier votre implantation (commentez l'ancienne) et vérifier que les tests marqués "Q7" passent.
Exercice 2 - Reversible2, le fils de la vengeance (à la maison)
Recopier l'interface Reversible dans une interface Reversible2.
On veut maintenant qu'elle implémente également l'interface List
et délègue les opérations à la structure de données sous-jacente si ces opérations ont un sens.
-
On peut remarquer que l'implantation de get peut être en O(n), dans un certain cas,
lequel ?
Comment faire pour éviter cela sachant que l'on ne connaît pas toutes les implantations
de java.util.List possible ?
Modifier votre implantation pour que les tests marqués "Q8" passent.
-
On souhaite que Reversible2 implante l'interface List.
On peut remarquer que parmi les méthodes mutables, il y a des méthodes qui ont un sens
car elles changent les valeurs sur place dans le Reversible2 et d'autre qui n'en n'ont pas.
Vous devez donc implanter la méthode replaceAll, la méthode set, la méthode sort
ainsi que les méthodes listIterator.
Bien sûr, l'astuce consiste à ne pas ré-implanter toutes ces méthodes mais seulement celles qui sont nécessaires,
les autres étant implantées à partir des méthodes que vous allez écrire.
Modifier votre implantation pour que les tests marqués "Q9" passent.
Note : vous pouvez utiliser AbstractList pour éviter de ré-écrire toutes les méthodes
de l'interface List.
© Université de Marne-la-Vallée