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

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).

Des tests unitaires correspondant à l'implantation sont ici : ReversibleTest.java.

  1. 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.

  2. 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 ?

  3. 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é.

  4. 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.

  5. 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).

  6. 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.

  7. 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.

Des tests unitaires correspondant à l'implantation sont ici : Reversible2Test.java.