Skip Navigation Links
Accueil
Java Standard EditionExpand Java Standard Edition
Java EE 5Expand Java EE 5
Visual Basic .Net 2005Expand Visual Basic .Net 2005
Visual C++ .Net 2005Expand Visual C++ .Net 2005
Visual C# .Net 2005Expand Visual C# .Net 2005
Cours ASP .Net 2.0Expand Cours ASP .Net 2.0
PostgresqlExpand Postgresql
LinuxExpand Linux
Visual Studio 2008Expand Visual Studio 2008
ASP 3.0 ClassiqueExpand ASP 3.0 Classique
Cours Javascript - DOM - DHTMLExpand Cours Javascript - DOM - DHTML
Cours AjaxExpand Cours Ajax
VBAExpand VBA
AssembleurExpand Assembleur
PerlExpand Perl
MembresExpand Membres
L'auteur du site
Nouveautés sur le site
Contacts
Plan du site
Accueil > Java Standard Edition > Les collections(7): AbstractMap(java.util)
____________________________________________________________________________________________________
Connexion

Les collections(7): AbstractMap(java.util)

Sommaire :

I) La classe AbstractMap (java.util)
II) L'implémentation de AbstractMap (java.util)
  II-1) L'unique constructeur
  II-2)entrySet(): la seule méthode abstraite
  II-3)Implémentation de get(Object key)
  II-4)Implémentation de remove(Object key)
  II-5)Implémentation de keySet()
  II-6)Implémentation de values()
  II-7) Implémentation de putAll
III) Les deux classes internes statiques fournies
  III-1) AbstractMap.SimpleEntry<K,V>
  III-2) AbstractMap.SimpleImmutableEntry<K,V>

En résumé:
- C'est une implémentation minimale de l'interface Map.
- Pour les maps non modifiables, il y a juste à définir la méthode entrySet. C'est sur la seule méthode abstraite à implémenter. Ce qui signifie que l'implémentation fournie est basée complètement sur le Set retourné par cette méthode, ce qui se comprend aisément, car on n'a pas notre structure interne de données dans AbstractMap, on s'appuie donc sur ce qu'on peut pour obtenir une implémentation qui nous fournisse la fonctionnalité, même si elle n'est pas la plus efficace.
- Pour les maps modifiables, il faut, de plus, redéfinir la méthode put(qui n'est pas abstraite), qui ne peut pas être batie en s'appuyant sur une brique qu'on aurait déjà.
La méthode remove est basée sur entrySet().iterator().remove(), qui doit par conséquent être implémentée par l'itérateur du set(c'est une opération optionnelle).

I) La classe AbstractMap (java.util)

Elle ne fait qu'implémenter l'interface Map. C'est son unique apport.

II) L'implémentation de AbstractMap (java.util)

II-1) L'unique constructeur

Remarquons d'abord qu'il n'y a qu'un unique constructeur, sans arguments, et qui ne fait rien:
Il n'était pas possible de fournir, à ce niveau, les constructeurs qui seront réellement utilisés par l'utilisateur de la classe. En effet, n'oublions pas que nous ne possédons pas la structure de données internes, nous ne pouvons pas faire, dans ces conditions, d'allocation dynamique de cette structure de données!

Le principe de AbstractMap, est d'utiliser le Set fourni par entrySet(). Ce Set sera utile notamment pour parcourir les valeurs de la Map. On a donc choisi de fixer un minimum à implémenter, ici cette méthode, et de baser toute l'implémentation de AbstractMap sur cette fonctionnalité minimale.

II-2)entrySet(): la seule méthode abstraite

entrySet() est la seule méthode abstraite:

II-3)Implémentation de get(Object key)

Tout part de la constatation qu'une map n'est rien d'autre qu'un set d'entry. Il suffit alors d'utiliser ce set équivalent dans les méthodes non implémentées de AbstractMap.

Par exemple, la méthode get(Object key):
On va se servir de l'itérateur. Un Set n'est pas ordonné, c'est pour cela qu'il n'y a pas de méthode qui permette d'accéder aux éléments, sauf iterator(), qui nous fournit un itérateur sur le set. Je ne parle pas de la méthode toArray() de Set.

On sépare le cas où la clé recherchée est null, et celui où elle ne l'est pas.
Dans le cas où elle vaut null, on parcourt les entrées du set jusqu'à trouver une clé = null. On arrête le parcours, et on retourne la valeur associée alors.

Le set est censé nous fournir un set d'entrée(Entry), qui respecte la structure des maps, c'est-à-dire qu'on ne peut pas avoir deux fois la même clé pour deux entrées (ce qui aurait été possible avec un set d'entrées). On fait donc comme si on ne pouvait pas avoir deux clés trouvées (de toutes façons on s'arrête à la première trouvée).

De même pour le cas où la clé recherchée est différente de null. On fait une boucle while sur le hasNext(). En cas de set vide, on s'arrête tout-de-suite. Sinon, on rentre dans la boucle au moins une fois. Quand on entre dans la boucle, on vérifie si l'élément est l'élément recherché ou pas. Si c'est le cas, on retourne la valeur(de type V) de l'entrée. On s'arrête dès qu'on a trouvé.

Le return null de fin est exécuté dans le cas où on n'a pas trouvé la clé.

II-4)Implémentation de remove(Object key)

Regardons à présent la méthode remove(Object key) de AbstractMap:
Tout est aussi basé sur le iterator de entrySet(), pour les mêmes raisons que la méthode get vue ci-dessus. De plus, la suppression est basée sur le entrySet().iterator().remove().
On aurait pu aussi se servir du remove(Object o) de Set, mais c'est plus facile pour nous d'utiliser le remove de l'iterator, car on parcourt le set pour retrouver le bon objet Entry.
Et de toutes façons, si le remove de iterator est implémenté, alors le remove de Set a dû être implémenté également. Ce qui signifie que cela ne nous aurait pas soulagé de la contrainte de demander une implémentation de remove.

Le code est sur le même principe que le get: logique car on recherche la valeur à supprimer, d'après sa clé. Le correctEntry==null dans la condition des deux while, permet de sortir de la boucle dès qu'on a trouvé. Avec le get, on n'avait pas ce problème car on faisait un return.

Puis il ne reste qu'à supprimer l'entrée, si on l'a trouvé( si correctEntry !=null ), par un i.remove(). On aura gardé auparavant l'ancienne valeur de l'entrée qu'on supprime, pour la retourner( on retourne null si pas d'entrée trouvée).

II-5)Implémentation de keySet()

Voyons la méthode KeySet:
Cette méthode utilise un attribut privé keySet, pour gardé le key set en mémoire, s'il a déjà été construit auparavant.
On comprend pourquoi le mettre transient: aucun intérêt de garder sa valeur lors d'une sérialisation. volatile sert à obliger la JVM à ne pas nous donner une valeur mise en cache, lorsqu'on lui demande la valeur de la propriété, et à aller chercher effectivement la valeur. Donc on est sûr
d'avoir la valeur la plus récente de la variable. Toutes les jvm ne prennent pas en compte ce mot-clé volatile. Il n'est pas beaucoup utilisé.

Le fonctionnement de keySet() est simple: si le keySet existe déjà, on retourne sa référence. Sinon, on en crée un avec une classe anonyme.

On voit ici un exemple de l'intérêt des implémentations minimales fournies par le framework, ici AbstractSet. On crée un set d'une classe anonyme héritant de AbstractSet, et dont on redéfinit les méthodes iterator(), size et contains Ces trois méthodes ne sont pas présentes du tout dans AbstractSet, elles proviennent de AbstractCollection.
size() et iterator sont abstract dans AbstractCollection. Pour contains, on le redéfinit.

La méthode iterator() se sert elle-même d'une classe anonyme qui implémente Iterator.
Cette classe anonyme a juste en attribut privé un iterator de Entry. Tout est basé sur celui-ci.
On fait un genre de filtre: le hasnext() appelle i.hasnext(). Le remove() appelle i.remove().
Seul le next fait quelque chose de plus: il renvoie uniquement la clé, au lieu de renvoyer toute l'entrée.

On constate que notre key set se base sur notre entry set, comme structure de donnée interne.
L'entry set est d'ailleurs déclaré en attribut privé de l'itérateur.

La méthode size() de notre AbstractSet ne fait qu'appeler size() de notre AbstractMap: notre key set a le même nombre d'éléments que notre map: le nombre d'entrées, qui équivaut au nombre de clés.
La méthode contains( Object key) de notre AbstractSet, appelle juste containsKey de AbstractMap: on veut juste savoir si le set contient une clé, ce qui revient à se demander si la map contient cette clé(on sait déjà le faire).

II-6)Implémentation de values()

La méthode values est basée exactement sur le même principe que keySet.
Ici, c'est une Collection<V> qui est retournée. Et on utilise AbstractCollection, et non AbstractSet. AbstractCollection, elle-aussi, n'a que deux méthodes abstraites, size() et iterator().
L'attribut privé est celui-ci:

II-7) Implémentation de putAll

putAll fait un "foreach". Pour chaque entrée de l'entry set relative à la mappe m passée en paramètre, elle l'ajoute, par un put, dans notre map.

Tout est basé sur l'entry set de la map passée en paramètre, mais ce n'est pas pour des raisons de méthodes non implémentées, comme pour nous, puisque la map passée en paramètre possède toutes ses méthodes(c'est un objet)!

III) Les deux classes internes statiques fournies

AbstractMap nous fournit deux classes internes statiques pour nos propres besoins personnels. Elles ne sont absolument pas utilisées dans AbstractMap.

III-1) AbstractMap.SimpleEntry<K,V>

On comprend l'intérêt: dans certains cas, on doit retourner des objets Entry. Pour cela, l'interface Entry ne suffit pas. C'est par exemple le cas quand on doit implémenter la méthode Map.entrySet().toArray.

Regardons son code:
La clé et la valeur sont en attributs privés. La clé est en final(ainsi elle est constante).
Il n'y a aucune difficulté dans le code.

III-2) AbstractMap.SimpleImmutableEntry<K,V>


Il s'agit d'une classe représentant une entrée immuable(la clé et la valeur ne peuvent être changées). Le code est le même que SimpleEntry, excepté pour la méthode SetValue, qui déclenche une UnsupportedOperationException. Ainsi que l'attribut privée value, qui est en final.

RETOUR HAUT