Structures mutables
par Jean-Christophe Filliâtre

 Login :  Mot de passe :

Le but de ce TD est de coder une structure de données semblable à LinkedList, sous la forme d'une classe Dequeue (pour double-ended queue).

Créer un projet td5. Dans ce projet, créer un package dequeue. Il contiendra la classe Dequeue. Les différentes classes de test seront placées dans le package par défaut.

Important : La plupart des programmes fournis pour tester votre code utilisent la construction assert de Java. Vous devez donc passer l'option -ea à la machine virtuelle Java (onglet VM Arguments de la configuration de lancement).

Structure de liste doublement chaînée

On adopte ici une structure de liste doublement chaînée, avec un accès immédiat au premier et au dernier élément :
             first                                                last
               |                                                   |
               V                                                   V
     +------+----+----+     +---+----+----+                  +---+----+------+
     |      | x1 | o------> |   | x2 | o------>   ...        |   | xn | null |
     | null |    |    | <-----o |    |    |   ...        <-----o |    |      |
     +------+----+----+     +---+----+----+                  +---+----+------+
On se donne une classe générique Cell<E> pour représenter les cellules de la liste doublement chaînée :
package dequeue;

class Cell<E> {
	final E element;
	Cell<E> prev, next;

	Cell(E e) {
		this.element = e;
		this.prev = this.next = null;
	}
}

Créer une classe publique Dequeue<E> dans Dequeue.java. Le type E est le celui des éléments. Dans cette classe, créer :

Par la suite, on maintiendra en permanence l'invariant suivant :
soit size == 0 et first == last == null,
soit size > 0 et first.prev == last.next == null et la liste contient exactement size éléments.
Le caractère privé des trois champs size, first et last est un moyen de protéger cet invariant.

Écrire un constructeur publique Dequeue() qui renvoie une liste ne contenant aucun élément.

Écrire les méthodes publiques suivantes :

Tester avec le code Test1.java (à placer dans le package par défaut).

Déposer ici votre fichier Dequeue.java :

Ajout et suppression d'éléments

Ajouter les méthodes publiques suivantes :

Ajouter ensuite les méthodes publiques suivantes :

Ces quatre dernières méthodes doivent lever l'exception prédéfinie NoSuchElementException si la liste est vide.

Tester avec le code Test2.java (à placer dans le package par défaut).

Déposer ici votre fichier Dequeue.java :

Affichage

Écrire une méthode publique String toString() qui renvoie une chaîne de caractères de la forme "{s1, s2, ..., sn}"s1, s2, etc., sont les chaînes de caractères obtenues avec la méthode toString des différents éléments. En particulier, si la liste est vide, cette méthode doit renvoyer la chaîne "{}". On notera qu'il n'y a pas de virgule (ni d'espace) après le dernier élément sn.

Tester avec le code Test3.java (à placer dans le package par défaut).

Déposer ici votre fichier Dequeue.java :

Itérateur

Nous allons maintenant faire en sorte de pouvoir parcourir les éléments de nos listes à l'aide de la construction for (E x : l) .... On rappelle qu'il s'agit là d'un sucre syntaxique pour

for (Iterator<E> it = l.iterator(); it.hasNext(); ) {
    E x = it.next();
    ...
}

Dans la classe Dequeue, il faut donc :

Tester avec le code Test4.java puis avec Mergesort.java (à placer dans le package par défaut).

Déposer ici votre fichier Dequeue.java :

Robustesse

A priori, rien ne nous empêche de modifier une liste (avec clear, addFirst, removeLast, etc.) alors qu'on est en train de la parcourir à l'aide d'un itérateur. Pour y remédier, on se propose d'invalider un itérateur dès lors que la liste qu'il parcourt est modifiée (quelle que soit la modification). Pour cela

Tester avec le code Test5.java (à placer dans le package par défaut).

Déposer ici votre fichier Dequeue.java :

Suppression pendant un parcours

Coder la méthode remove de la classe Iter. Elle doit permettre de supprimer de la liste l'élément que l'on vient de récupérer avec la méthode next de l'itérateur. Si un tel élément n'existe pas ou n'existe plus, alors remove doit lever l'exception IllegalStateException. Comme pour next, l'exception ConcurrentModificationException prime sur l'exception IllegalStateException.

L'utilisation de remove doit invalider l'utilisation de tout autre itérateur sur la liste. Il faut donc incrémenter le numéro de version de la liste (et donc également celui attendu par l'itérateur dont on vient d'appeler la méthode remove).

Tester avec le code Test6.java (à placer dans le package par défaut).

Déposer ici votre fichier Dequeue.java :