:: Enseignements :: Master :: M1 :: 2025-2026 :: Java Avancé ::
[LOGO]

Examen de Java Avancé 2025 - 2026 - session 1


Le but de ce TP noté est d'implanter une classe IndexedMap qui est une liste non modifiable d'éléments que l'on veut voir comme une Map.

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.
La javadoc 25 est disponible à l'adresse : https://igm.univ-mlv.fr/~juge/javadoc-25/.
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 - IndexedMap

La classe IndexedMap est une classe générique non modifiable qui stocke des éléments non nuls, chaque élément possède un index, 0 pour le premier élément, 1 pour le second, etc..., et cet index est utilisé pour obtenir les éléments.
Après une première implantation, notre but sera de rendre cette classe efficace en termes de performance.

La classe IndexedMap possède les opérations suivantes :
  • un méthode of(element0, element1, ...) qui créé une instance de IndexedMap initialisée avec ces éléments.
  • la méthode size qui renvoie le nombre d'éléments.
  • la méthode getValueOrDefault(index, defaultValue) qui permet d'obtenir un élément en fonction de son index, ou qui renvoie la valeur par défaut s'il n'y a pas d'élément à l'index indiqué.
  • une méthode keySet qui renvoie un ensemble des index valides, par exemple, si une IndexedMap possède 3 éléments, keySet renvoie l'ensemble (0, 1, 2).
  • la méthode values() qui renvoie une liste des éléments.
  • la méthode forEach(function) qui appelle la fonction avec l'index de l'élément ainsi que l'élément pour chaque élément dans l'IndexedMap.
  • la méthode stream() qui renvoie les couples index/élément pour chaque élément sous forme d'un Stream.

Voici un exemple d'utilisation
var map = IndexedMap.of("apple", "banana", "cherry");

IO.println(map.size());  // 3
IO.println(map.getValueOrDefault(0, "default"));  // apple
IO.println(map.keySet());  // [0, 1, 2]
IO.println(map.values());  // [apple, banana, cherry]
IO.println(map.get(1));  // banana
IO.println(map.stream().map(e -> "" + e).toList());  // [0=apple, 1=banana, 2=cherry]
     

Des tests unitaires correspondant à l'implantation sont ici : IndexedMapTest.java
Note : comme on utilise les tests unitaires JUnit sans Maven, dans la configuration de votre projet, il faut ajouter la librairie JUnit 5, soit à partir du fichier IndexedMapTest.java, en cliquant sur l'annotation @Test et en sélectionnant le quickfix "Fixup project ...", soit en sélectionnant les "Properties" du projet (avec le bouton droit de la souris sur le projet) puis en ajoutant la librairie JUnit 5 (jupiter) au ClassPath.

  1. Dans un premier temps, on veut implanter la méthode of() pour créer des instances de IndexedMap ainsi que les méthodes size() et getValueOrDefault().
    L'implantation doit utiliser une java.util.List en interne, pour stocker les éléments.
    Écrire la classe IndexedMap, sa méthode of, ses méthodes size et getValueOrDefault.
    Vérifier que les tests marqués "Q1" passent.

  2. On souhaite maintenant implanter la méthode keySet() qui renvoie l'ensemble des index pour les éléments de l'IndexedMap sous forme d'une vue.
    Implanter la méthode keySet().
    Vérifier que les tests marqués "Q2" passent.
    Rappel : en Java, on peut implanter facilement l'interface Set en utilisant la classe abstraite AbstractSet (de même pour List et AbstractList et Map et AbstractMap).

  3. On souhaite implanter la méthode values qui renvoie une liste non-modifiable des éléments sous forme d'une vue.
    Implanter la méthode values.
    Vérifier que les tests marqués "Q3" passent.

  4. On souhaite ajouter une méthode forEach(function) qui, pour chaque élément, appelle la fonction avec en premier paramètre l'index et en second paramètre l'élément.
    Quelle interface fonctionnelle doit-on utiliser ici, sachant que l'on permet le boxing, car dans le futur, on voudra implanter l'interface Map (cf. question suivante).
    Implanter la méthode forEach().
    Vérifier que les tests marqués "Q4" passent.

  5. On souhaite maintenant que IndexMap implante l'interface Map.
    Modifier le code de la classe IndexMap en conséquence.
    Vérifier que les tests marqués "Q5" passent.

  6. Si vous ne l'avez pas déjà fait à la question précédente, les méthodes get() et getOrDefault() n'ont pas la bonne complexité pire cas, comment doit-on résoudre ce problème ?
    Faire les changements qui s'imposent.
    Vérifier que les tests marqués "Q6" passent.

  7. En fait, les deux méthodes précédentes en sont pas les seules qui n'ont pas la bonne complexité.
    Regarder les tests, en déduire en les méthodes qui n'ont pas la bonne complexité et corriger cela.
    Vérifier que les tests marqués "Q7" passent.

  8. On souhaite ajouter une méthode stream() qui renvoie les couples index/éléments dans le même ordre que la méthode forEach().
    Implanter la méthode stream() sachant que pour l'instant seule la version séquentielle nous intéresse.
    Vérifier que les tests marqués "Q8" passent.

  9. Modifier l'implantation précédente pour que l'implantation parallèle sépare bien les Spliterator en deux indépendamment du nombre d'éléments (supérieur à 1).
    Vérifier que les tests marqués "Q9" passent.