:: Enseignements :: Master :: M1 :: 2014-2015 :: Java Avancé ::
[LOGO]

Examen de Java Avance sur table


Exercice 1 - Questions de cours (6)

Répondez aux questions suivantes en deux ou trois phrases, pas plus.

  1. Avec le code suivant:
         public class A {
           void m() { }
         
           static class B extends A {
             void m() { }
           }
         
           public static void main(String[] args) {
             A a = new B();
             a.m();
           }
         }
         
    Completer les phrases ci-dessous:
    • a est une référence sur une ______ de la classe B
    • A est un ______-type de B
    • B est un ______-type de A
    • La méthode m de B est un ______ de la méthode m de A
  2. Dans le code de la question précédente, que ce passe t'il si on enlève le mot-clé static devant la définition de la classe B.
    Pourquoi ?
  3.       class Foo {
            static class A {
              public boolean compare(A a) {
                return true;
              }
            }
            
            static class B extends A {
              public boolean compare(B b) {
                return false;
              }
            }
            
            public static void main(String[] args) {
              A a = new B();
              System.out.println(a.compare(new B()));
            }
          }
          
    Qu'affiche le code ci-dessus ?
    Pourquoi ?
  4. Expliquer pourquoi le code suivant n'est pas thread-safe en prenant un exemple concret.
            public class StringStack {
              private final ArrayDeque<String> stack = new ArrayDeque<>();
              
              public void push(String s) {
                stack.push(s);
              }
              
              public String pop() {
                if (stack.isEmpty() {
                  throw new IllegalStateException();
                }
                return stack.pop();
              }
            }
          
  5. Modifier le code de la question précédente pour le rendre thread-safe.

Exercice 2 - Chef, j'ai besoin d'un remplaçant (6)

On cherche à remplacer des élements dans un tableau puis dans une liste

  1. Lire le sujet en entier !
  2. Ecrire une méthode replaceAndAddOne qui prend un tableau d'entiers en argument (et ne renvoie rien) et ajoute 1 à toutes les cases du tableaux
  3. Ecrire une méthode replaceAndMultiplyBy2 qui prend un tableau d'entiers en argument (et ne renvoie rien) et multiplie par 2 toutes les cases du tableaux.
  4. Bien sûr, le code est quasiment identique, on souhaite partager le code commun. Ecrire une méthode replace qui prend un tableau d'entier et une fonction permettant de calculer les nouvelles valeurs des cases du tableau.
    Indiquer, de plus, le nouveau code de replaceAndAddOne et replaceAndMultiplyBy2.
    On vous demande d'écrire le code sans utiliser les classes/interfaces des packages java.util, java.util.function et java.util.stream.
  5. On souhaite écrire les mêmes méthodes que précédemment mais prenant une liste (de type java.util.List en paramètre (et retournant toujours rien). La liste sera paramètrée pour marcher avec n'importe quel type et pas uniquement des entiers.
    Indiquer le nouveau code des méthodes replace, replaceAndAddOne et replaceAndAddOne
    Note: attention aux wildcards !
  6. Expliquer pourquoi utiliser List.get (et List.set) ici est affreux (si si, vraiment affreux). Si vous ne l'avez pas déjà fait, indiquer le code de replace en utilisant iterateur pour implanter replace.
    Note: il faut utiliser l'iterateur spécifique au liste, pas l'itérateur de base !

Exercice 3 - HashGen (8)

On souhaite écrire une table de hachage d'un genre un peu particulier. Premièrement, il sera possible uniquement d'ajouter des élements dans la table de hachage, pas d'en supprimer (au moins au début). De plus, à chaque fois qu'un élement sera ajouté, on incrémentera un compteur appelé compteur de génération, l'élement sera stocké avec sa valeur de génération.
De plus, une opération appeler snapshot permet à partir de la table HashGen d'obtenir une Map contenant toutes les valeurs insérées avant l'appel à snapshot et qui ne contiendra pas les valeurs ajoutées après l'appel à snapshot.
Bien sûr, il est facile d'implanter snapshot en copiant toutes les valeurs dans une nouvelle Map et de renvoyer celle-ci mais ce n'est pas très efficace. L'idée en plutôt que la Map renvoyé stocke juste le numéro de version au moment où on appel snapshot et que celle-ci montre parmis les valeurs HashGen uniquement celles ayant une génération de valeur inférieur. Comme cela, les valeurs ne sont pas stockées dans plusieurs endroits mais uniquement dans HashGen.
Enfin, pour simplifier le problème même si c'est mal, la table de hachage ne se redimensionnera pas.
    public class HashGen {
      private final HashEntry[] entries = new HashEntry[32];
    
      static class HashEntry {
        final Object key;
        Object value;
        final Entry next;
        
        HashEntry(Object key, Object value, Entry next) {
          this.key = key;
          this.value = value;
          this.next = next;
        }
      }
      
      public Object get(Object key) {
        Objects.requireNonNull(key);
        int index = key.hashCode() & 0b11111;
        HashEntry head = entries[index];
        for(Entry e = head; e != null; e = e.next) {
          if (e.key.equals(key)) {
            return e.value;
          }
        }
        return null;
      }
      
      public void put(Object key, Object value) {
        Objects.requireNonNull(key);
        Objects.requireNonNull(value);
        int index = key.hashCode() & 0b11111;
        HashEntry head = entries[index];
        for(Entry e = head; e != null; e = e.next) {
          if (e.key.equals(key)) {
            e.value = value;
            return;
          }
        }
        entries[index] = new HashEntry(key, value, head);
      }
    }
   

  1. Lire le sujet en entier.
  2. Indiquer le code de HashGen en ajoutant le numéro de génération et en générifiant le code de HashGen (avec une variable de type K pour le type des clés et V pour les type des valeurs).
    Rappel: il est interdit de créer un tableau de type paramétré !
  3. Implanter la méthode snapshot sachant qu'elle doit retourner une Map et qu'il existe une classe AbstractMap qui permet d'implanter une Map read-only en implantant uniquement la méthode entrySet.
    Attention à la complexité de la méthode size du Set renvoyé par entrySet.
  4. Pourquoi doit on aussi implanter la méthode get de AbstractMap ?
    Indiquer le code de cette méthode.
  5. Comment se comporte votre code si on fait deux put avec la même clé et pas la même valeur ?
           ...
           hashGen.put("foo", "bar");
           Map<String, String> map = hashGen.snapshot();
           hashGen.put("foo", "baz");
         
    Vérifier que la map contient bien la valeur bar pour la valeur foo
    Si ce n'est pas le cas, comment changer votre code ?