:: Enseignements :: Master :: M1 :: 2020-2021 :: Java Avancé ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) | Examen de Java Avancé - session 2 |
Le but de ce TP noté est d'implanter une structure de données appelée FastSearchSeq
qui permet de stocker des éléments de façon séquentielle (c'est à dire les uns derrière les autres, comme dans une liste). Cependant, lors de la recherche d'un élément, si celui-ci est trouvé, on le déplace vers le début
de la structure, pour que les recherches suivantes soit accélérées.
Donc l'ordre des éléments ne dépend pas uniquement de l'ordre d'insertion mais aussi de l'ordre dans lequel sont faites les recherches.
Vos sources Java produites pendant l'examen devront être placées sous le répertoire EXAM
de votre compte ($HOME) (qui est vide dans l'environnement de TP noté).
Sinon, elles ne seront pas récupérées.
Tout document papier est proscrit.
Vous pouvez consulter la javadoc à travers Eclipse (onglet Javadoc), en utilisant
jdoc dans un terminal
Sinon la doc est là :
/usr/local/apps/java15/docs/api/index.html.
Les seuls documents électroniques autorisés sont les supports de cours à l'url
http://igm.univ-mlv.fr/~forax/ens/java-avance/cours/pdf/.
Vous avez le droit de lire le sujet jusqu'au bout, cela vous donnera une bonne idée de là où on veut aller !
Exercice 1 - FastSearchSeq
La classe
FastSearchSeq est un conteneur d'éléments non null
qui stocke ses éléments dans un
tableau.
La classe possède, entre autres, les méthodes suivantes :
-
add qui ajoute un élément dans la structure de données.
Un même élément peut être ajouté plusieurs fois.
-
size qui renvoie le nombre d'éléments précédemment ajoutés à la structure de données.
-
contains qui permet de tester si un objet appartient à la structure de données.
De plus, si l'objet recherché est un élément de la structure de données,
celui-ci va être déplacé vers le début de la structure de données pour accélérer les recherches ultérieures.
-
forEachIndexed qui prend en paramètre une fonction à deux paramètres et appelle celle-ci
avec chaque élément de la structure de données et l'indice de la position actuelle de cet élément dans le tableau.
-
stream qui renvoie un Stream des éléments dans le même ordre que forEachIndexed.
Il doit aussi être possible de parcourir les éléments de la structure de données en utilisant
une boucle
for comme ceci :
FastSearchSeq<String> seq = ...
for(String s: seq) {
...
}
La classe FastSearchSeq est paramétrée par le type des éléments qu'elle contient et
ces éléments ne peuvent pas être null.
-
Dans un premier temps, on souhaite implanter la classe FastSearchSeq
dans le package fr.umlv.util avec deux champs,
le tableau des éléments dans un champ array, ainsi qu'un champ size
qui indique le nombre d'éléments dans la structure de données.
Le tableau sera initialisé avec une taille de 16, et pour l'instant on ne vous
demande pas que la structure de données gère plus de 16 éléments.
Écrire la classe FastSearchSeq et implanter les méthodes add et size.
Vérifier que les tests unitaires marqués "Q1" passent.
-
On souhaite pouvoir afficher le contenu de la structure de données (les éléments séparés par des virgules suivies d'un espace) avec le code suivant :
FastSearchSeq<String> seq = ...
System.out.println(seq);
Modifier votre code en conséquence sachant que l'on demande que votre implantation
utilise un Stream.
Vérifier que les tests unitaires marqués "Q2" passent.
Indice : la classe java.util.Arrays contient plusieurs méthodes nommées stream.
Note : si vous n'arrivez pas à implanter le code requis avec un Stream, essayer de faire différemment mais vous n'aurez pas tous les points correspondant à la question.
-
On veut maintenant que le tableau s’agrandisse automatiquement lorsque la structure
de données doit contenir plus de 16 éléments, en s’agrandissant d'un facteur de 2 à chaque redimensionnement.
Changer le code en conséquence et vérifier que tests unitaires marqués "Q3" passent.
-
On souhaite écrire une méthode contains qui renvoie vrai si un objet est contenu dans la structure de donnée et faux sinon.
Implanter la méthode contains en utilisant là aussi un Stream.
Vérifier que les tests unitaires marqués "Q4" passent.
-
Nous allons modifier le comportement la méthode contains, commencez par mettre votre précédente version en commentaire.
En fait, on veut accélérer la recherche dans le cas où un même élément est recherché
plusieurs fois de suite. Pour cela, lors d'une recherche, si l'objet recherché est présent, on va le déplacer vers le début du tableau.
Nous allons utiliser l'algorithme suivant (pseudo code) :
Après la recherche, si l'élément est présent à l'index index1
On calcule le logarithme du nombre d'éléments (* voir indication ci-dessous *)
Si la valeur d'index1 est inférieure ou égale à ce logarithme,
alors l'élément est déjà devant dans le tableau, donc on ne fait rien.
Sinon
on tire un nombre aléatoire random (* voir indication ci-dessous *)
on calcule une nouvelle position index2 comme étant le reste de la division
entière de random par la valeur du logarithme calculé précédemment.
on permute les éléments aux index index1 et index2.
L'idée est que si l'élément n'est pas au début du tableau, on le déplace au début du tableau,
mais pas toujours à la même place car lors du déplacement, on déplace aussi l'ancien élément vers la fin du tableau. Pour cela, on utilise une valeur pseudo aléatoire.
Indication 1 : pour calculer une approximation entière du logarithme de façon rapide, vous devez utiliser le fait c'est équivalent à 31 moins le nombre de 0 en début du nombre d'éléments (Integer.numberOfLeadingZeros() en Java).
Indication 2 : pour obtenir un nombre aléatoire de façon rapide, on utilise la valeur renvoyée par la méthode nextRandom suivante, en déclarant un champ random de type int initialisé avec 1.
private int nextRandom() {
var x = this.random;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
return this.random = x;
}
Modifier votre code pour implanter l'algorithme ci-dessus (et n'oubliez pas de commenter la partie de code correspondant à l'ancienne version de contains).
Vérifier que les tests unitaires marqués "Q5" passent.
Note : si vous n'y arrivez pas, passez aux questions suivantes !
-
On souhaite pouvoir faire une boucle sur les éléments présents dans la structure de données avec le code ci-dessous
FastSearchSeq<String> seq = ...
for(String s: seq) {
...
}
De plus, si jamais la structure de données est altérée pendant le parcours, c'est à dire si on ajoute
un élément dans le code à l'intérieur de la boucle, l'exception habituelle doit être levée.
On ne considère pas que les modifications apportées par un appel à contains altèrent la
structures de données.
Modifier votre code en conséquence et vérifier que les tests unitaires marqués "Q6" passent.
Indice : la seule façon d'altérer la structure est d'ajouter un élément. On peut donc simplement vérifier si la taille change.
-
On souhaite ajouter une méthode forEachIndexed qui permet de parcourir tous les
éléments présents dans la structure de données ainsi que leur indice actuel dans le tableau.
Par exemple, si on a ajouté les éléments
"j'aime", "le", "Java" à l'instance seq,
seq.forEachIndexed((element, index) -> {
System.out.println(element + " " + index);
});
alors le code ci-dessus affiche
j'aime 0
le 1
Java 2
Implanter la méthode forEachIndexed.
Vérifier que les tests unitaires marqués "Q7" passent.
Note : vous aurez besoin de définir votre propre interface fonctionnelle
que l'on va appeler IndexedConsumer.
-
On souhaite ajouter une méthode onlyElement dans l'interface fonctionnelle IndexedConsumer
de telle sorte qu'avec la même structure de données contenant "j'aime", "le", "Java",
le code suivant
seq.forEachIndexed(IndexedConsumer.onlyElement(System.out::println));
affiche uniquement les éléments sans leur indice :
j'aime
le
Java
Ajouter la méthode onlyElement et vérifier que les tests unitaires marqués "Q8" passent.
-
On souhaite ajouter une méthode stream qui renvoie les éléments de 0 à la taile de la structure
sous forme d'un Stream parallélisable (la taille est la taille au moment où la
méthode stream est appelée).
Contrairement au code pour le support de la boucle, on ne vous demande pas de détecter les modifications
de la structure de données.
Modifier le code que vous avez écrit précédemment pour que la méthode stream
renvoie un Stream qui sache se paralléliser.
Vérifier que les tests unitaires marqués "Q9" passent.
© Université de Marne-la-Vallée