Jakarta Commons CollectionsDate de publication : 28/06/2005 , Date de mise a jour : 28/06/2005 Licence : CC-by-SA
Par
Sébastien Le Ray (Accueil) Nous continuons la série d'articles sur les API Commons du projet Apache Jakarta en nous intéressant aujourd'hui aux commons collections. I. Introduction II. Concepts II.A. Design-Pattern Decorator II.B. Décorateurs commons collections II.C. Catégories de sous-interfaces communes III. Entités support III.A. Comparator III.B. Comparable IV. Functors IV.A. Tranformer IV.A.1. Implémentation utilisables directement IV.A.2. Décorateurs IV.B. Predicate IV.B.1. Implémentations utilisables directement IV.B.2. Décorateurs IV.C. Factory V. Bag VI. BidiMap VII. Buffer VIII. List IX. Map X. Set XI. Autres XII. Remerciements XIII. Téléchargements I. Introduction
Les Commons Collections sont des extensions développées par Jakarta pour compléter le framework
Collections du JDK de Sun. Elles définissent un certain nombre d'interfaces ainsi
qu'une multitude d'implémentations relatives au comportement et à la manipulation des
collections. Nous n'entrerons pas dans le détail des closures
qui offrent une autre approche de la manipulation des collections.
Après une présentation des concepts clés et des entités supportant le fonctionnement de l'API,
nous décrirons, package par package, les fonctionnalités offertes pour chaque type de collection.
Notez que l'organisation des packages peut sembler assez désordonnée. En effet,
l'API a été modifiée et le découpage en packages a été entièrement revu. On trouve donc des
doublons, des classes qui n'ont pas encore été déplacées mais qui le seront peut-être bientôt.
Regardez la JavaDoc, vous verrez qu'un certain nombre de classes sont taggées deprecated,
ce qui signifie qu'elle vont être déplacées prochainement.
Ce tutoriel est basé sur la version 3.1 des commons collections, j'essaierai de mettre à jour
ce tutoriel si une nouvelle version sort un jour. Le passage à la version 4 devrait faire le
ménage dans les paquetages et n'assurera donc plus la compatibilité avec les versions antérieures
à la 3.
Pour certaines entités, vous avez le choix entre un constructeur et une méthode getInstance.
Il est préférable d'utiliser cette dernière car elle est généralement optimisée et valide les paramètres
qui lui sont passés.
II. ConceptsII.A. Design-Pattern Decorator
Afin d'apporter des fonctionnalités supplémentaires, les commons collections utilisent
massivement le design-pattern Decorator (les entités suivant ce design-pattern
seront appelées décorateurs). Les types de décorateurs fournis par les commons collections
sont décrits plus loin dans ce document mais une rapide présentation de ce principe
peut être utile avant de continuer.
Un décorateur est utilisé pour ajouter des responsabilités à un objet, l'objet décoré,
tout en respectant la même interface que celui-ci. Pour une description
plus en détails de ce Design-Pattern référez vous à
l'article
à ce sujet sur developpez.com.
II.B. Décorateurs commons collections
Les commons collections viennent avec un certain nombre de décorateurs standards. Certains peuvent
s'appliquer à toutes les Collections, d'autres uniquement à un certain type de Collection.
Le nom du décorateur est de la forme TypeDeDécorateurTypeDuDecoré. Par exemple,
un décorateur de type Typed pour les Bags s'appellera TypedBag.
Les décorateurs ne définissent qu'une seule méthode en plus de celles de l'objet décoré :
decorate, dont les arguments dépendent du type de décorateur. Il existe deux façons
d'obtenir un décorateur, soit en appelant cette méthode :
soit en utilisant les classes utilitaires de la forme TypeEntiteUtils :
Voici la description des différents types de décorateurs. Ne vous inquiétez pas si vous ne comprenez
pas tout tout de suite, cela deviendra plus clair dans la suite de cet article.
Voici un tableau résumant les décorateurs disponibles pour chaque interface majeure, ces interfaces
seront détaillées plus loin dans ce document) :
Il existe des décorateurs qui sont propres à un type d'interface en particulier, nous les
présenterons en même temps que l'interface concernée.
II.C. Catégories de sous-interfaces communes
Certaines interfaces sont dérivées en sous-interfaces destinées à assurer un comportement
particulier qui ne pourrait être mis en place via un décorateur. Bien qu'elles soient
propres à leur interface mère, on peut classer ces sous interfaces en deux grands types dont
le comportement est comparable. Ces sous-interfaces sont reconnaissables à leur nom :
Sorted et Ordered.
Les collections de types Sorted sont similaires à celles proposées par le
Framework Collections du JDK (java.util.SortedMap et java.util.SortedSet).
Elles assurent que les éléments sont maintenus triés (soit par le Comparator passé
au constructeur, soit selon l'ordre naturel auquel cas ils doivent implémenter
l'interface java.util.Comparable). L'API commons collections introduit les
interfaces SortedBag et SortedBidiMap.
Les collections de type Ordered sont introduites par les commons collections.
Elles assurent que le parcours de la collection via un Iterator se fait
dans un certain ordre. Il est possible de parcourir ce type d'entités dans les deux
sens en utilisant un OrderedIterator, elles permettent également l'accès à la
première et à la dernière clé via les méthodes firstKey() et
lastKey(). L'API commons collections définit les interfacs OrderedMap
et OrderedBidiMap.
III. Entités support
Les commons collections utilisent un certain nombre d'entités annexes utilisées au sein
des méthodes. Nous les présentons ici.
III.A. Comparator
Cette interface n'est pas définie directement par les commons collections, mais dans
le package java.util. Néanmoins, elle est largement utilisée par l'API de
Jakarta. Comme son nom l'indique, elle sert à comparer deux objets. L'interface
Comparator définit deux méthodes :
III.B. Comparable
Alors que Comparator est une interface destinée à donner des entités distinctes,
Comparable est une interface destinée à être implémentée par un objet classique.
C'est-à-dire que si l'on a un objet Chaise, Comparator donnera un objet de
la forme ChaiseComparator qui compare deux chaises, alors que Comparable sera implémentée
directement par Chaise ce qui permettra de comparer directement une Chaise à
une autre.
Comparable ne définit qu'une seule méthode, compareTo(Object o) qui retourne un entier
négatif si l'objet considéré est inférieur à l'objet passé en paramètre, zéro s'il sont égaux
et un entier positif si l'objet considéré est plus grand que l'objet passé en paramètre.
IV. Functors
Les functors sont un ensemble d'interfaces destinées à apporter des fonctionnalités
supplémentaires aux collections. Les interfaces sont définies dans le package
org.apache.commons.collections, les implémentations dans org.apache.commons.collections.functors.
IV.A. Tranformer
L'interface Transformer permet de transformer un objet en un autre selon des critères que
l'utilisateur définit dans les implémentations.
La seule méthode définie par cette interface est tranform(Object o) qui doit renvoyé
l'objet correspondant à la transformation de celui passé en paramètre.
L'API commons collections propose plusieurs implémentations de cette interface. Certaines sont
utilisables directement, d'autres sont des décorateurs.
IV.A.1. Implémentation utilisables directementImplémentations Utilisables Directement
Les Transformer dont le constructeur ne prend pas de paramètre sont des Singletons, ils disposent
donc d'une méthode getInstance() et ne peuvent pas être instanciés. Les autres Transformers
peuvent être obtenus soit via une instanciation classique, soit en appelant la méthode getInstance
avec les mêmes paramètres que ceux que l'on aurait passés au constructeur.
IV.A.2. Décorateurs
Comme pour les Transformers classiques, les décorateurs peuvent être instanciés directement, ou il est
possible de passer par une méthode getInstance. Notez que cette dernière offre plus de fonctionnalités.
IV.B. Predicate
L'interface Predicate est définie dans le package org.apache.commons.collections.
Elle est utilisée pour évaluer un objet, c'est-à-dire vérifier qu'il remplit bien certaines
conditions (à définir par l'utilisateur).
La seule méthode définie par l'interface est evaluate(Object o) qui renvoie true
si l'objet remplit les conditions du Predicat, false sinon.
L'API commons collections définit un certain nombre de Predicats. Certains sont utilisables
directement, d'autres sont des décorateurs.
IV.B.1. Implémentations utilisables directement
La plupart des Predicats utilisables directement sont des Singletons, ils possèdent
donc tous une méthode getInstance et ne peuvent pas être instanciés.
EqualPredicate, IdentityPredicate, InstanceOfPredicate, TransformerPredicate
UniquePredicate disposent d'une telle méthode mais ne sont pas des singletons,
les paramètres indiqués pour le constructeur peuvent également être passés à getInstance.
Vous pouvez bien sûr définir vos propres Predicates en implémentant l'interface.
IV.B.2. Décorateurs
Les Predicats décorateurs permettent de combiner plusieurs Predicats en
effectuant des opérations logiques.
IV.C. Factory
L'interface org.apache.commons.collections.Factory met en application le design-pattern
du même nom. Elle ne définit d'une seule méthode : create qui doit renvoyer un objet.
C'est au développeur de définir les caractéristiques de cet objet.
L'API commons collections définit un certain nombre d'implémentations de référence situées dans le
package org.apache.commons.collections.functors :
V. Bag
L'interface org.apache.commons.collections.Bag définit une collection qui peut contenir
plusieurs fois le même objet et permet de compter le nombre de fois où un objet apparaît.
Certaines méthodes sont en violation par rapport au contrat spécifié par l'interface
Collection du JDK ce qui peut dérouter les habitués. Voici une rapide présentation des
méthodes :
Les autres méthodes héritées de collections n'ont pas de particularité. En plus de ces méthodes
classiques, Bag définit d'autres méthodes qui lui sont propres :
Les entités liées à l'interface Bag sont définies dans le package
org.apache.commons.collections.bag.
L'implémentation de référence de l'interface Bag est la classe HashBag.
Celle-ci utilise en interne une Map pour effectuer la correspondance objet <-> nombre d'occurences.
Pour ce qui est de Sortedbag, l'implémentation de référence est TreeBag, son comportement
est strictement identique au HashBag (ils étendent tous deux AbstractMapBag) si ce n'est
qu'il utilise une TreeMap en interne.
VI. BidiMap
L'interface org.apache.commons.collections.BidiMap définit une Map qui permet l'accès aussi
bien dans le sens clé -> valeur que dans le sens valeur -> clé. Cela impose une
restriction : une valeur ne peut se trouver plusieurs fois dans la Map. L'interface définit
des méthodes supplémentaires spécifiques :
Par ailleurs, le comportement de put(Object cle, Object value) est modifié afin de garantir l'unicité des clés
et des valeurs. Si la clé existe déjà, la valeur correspondante est remplacée, si la valeur existe déjà,
la clé correspondante est remplacée.
Les différentes entités liées à l'interface BidiMap se trouvent dans le package
org.apache.commons.bidimap.
L'implémentation de référence pour l'interface BidiMap est la classe
DualHashBidiMap qui utilise deux HashMap en interne afin de faire
la correspondance clé <-> valeur.
La classe BidiMap a son équivalent Sorted avec la classe SortedBidiMap. Contrairement à ce
que l'on pourrait croire, elle n'utilise pas deux TreeMap en interne mais bénéficie d'un algorithme
de recherche entièrement recodé, basé sur la recherche au sein d'une TreeMap. Cette approche a été choisie
afin d'éviter le supplément de mémoire nécessaire au stockage de deux TreeMap lorsque le nombre
de données est important.
VII. Buffer
L'interface org.apache.commons.collections.Buffer définit une Collection pour laquelle
l'ordre de suppresion des éléments est bien défini. Cela permet de créer des files FIFO ou des files
à priorités. Deux méthodes spécifiques sont définies en dehors de celles de Collection
Le nombre d'implémentations de l'interface Buffer est assez important, elles sont regroupées
avec les autres entités liées à l'interface Buffer dans le package org.apache.commons.collections.buffer.
Nous les décrivons ici
en insistant sur les particularités de chacune. Excepté lorsque le Buffer est décoré par un
BlockingBuffer, toute tentative de get() ou de remove() sur
un Buffer vide soulève une BufferUnderFlowException.
FifoBuffer définit une file FIFO (First In, First Out, file d'attente), c'est-à-dire
que les éléments sont supprimés dans l'ordre d'ajout. On trouve trois variations de l'implémentation :
L'itération se fait dans le même ordre que la suppression.
L'implémentation de la file à priorité est fournie par la classe org.apache.commons.collections.PriorityBuffer
qui maintient les éléments triés grâce au Comparator passé au constructeur ou s'il n'y en a pas, selon l'ordre
naturel (les éléments doivent alors implémenter java.util.Comparable). Notez que contrairement aux FifoBuffers,
l'itération ne se fait pas dans l'ordre de suppression.
Aucune des implémentations précédentes n'est synchronisée, il est possible de passer par le décorateur
SynchronizedBuffer pour pallier à cela.
Les Buffers disposent d'un décorateur qui leur est propre, il s'agit du BlockingBuffer. Celui-ci
permet d'implémenter un schéma producteur/consommateur similaire aux pipes sous Unix. Lors d'un get() ou
d'un remove() sur un Buffer vide, le Thread se met en attente jusqu'à ce qu'un élément soit
disponible ou qu'il soit interrompu. Cette implémentation est synchronisée.
Enfin, l'interface Buffer est implémentée par un nouveau type de pile, la classe
org.apache.commons.collections.ArrayStack. Celle-ci est identique à la classe java.util.Stack
mais utilise une ArrayList en interne, ses méthodes ne sont donc pas synchronisées et elle est
donc plus rapide que l'implémentation originale.
VIII. List
L'API commons collections définit diverses implémentations de l'interface java.util.List. Celles-ci
se trouvent dans le package org.apache.commons.collections.list.
CursorableLinkedList définit un type de liste pour lequel les modifications apportées à la liste
sont reflétées au niveau des itérateurs et réciproquement.
NodeCachingLinkedList est une liste qui implémente un système de cache. Lors de la suppression d'un
noeud de la LinkedList, celui-ci est mis en cache s'il reste de la place. Lors de l'ajout d'un noeud,
le cache est interrogé, si un noeud est disponible il est supprimé du cache et renvoyé, sinon, un nouveau noeud
est créé.
TreeList est une implémentation de List optimisée pour l'ajout et la suppression à n'importe
quel indice. Néanmoins, l'ArrayList est plus efficace pour les autres opérations et elle
occupe moins de place en mémoire. La TreeList est donc une implémentation à privilégier dans le cas
de mises à jour fréquentes de la liste (insertions et non pas ajouts en fin).
FixedSizeList est un décorateur qui interdit toute opération susceptible de modifier la taille de
la liste (famille des add et des remove) en soulevant une UnsupportedOperationException.
SetUnique est un décorateur qui empêche les doublons (à la manière d'un Set) tout en permettant de
conserver les propriétés des listes (notamment au niveau de l'itération).
IX. Map
L'API commons collections introduit plusieurs améliorations en ce qui concerne les Maps (définies
dans le org.apache.commons.collections.map). Parmi celles-ci,
on trouve un nouveau concept : le MapIterator. Comme son nom l'indique, il s'agit d'un itérateur
qui permet de parcourir une Map. En plus des méthodes traditionnelles hasNext, next
et remove, l'interface définit les méthodes getKey, getValue qui renvoient respectivement
la clé et la valeur correspondantes à la position en cours, setValue(Object o) qui positionne la valeur
de la position en cours. A noter que next avance d'une position et renvoie la valeur de la clé correspondant
à la position suivante. L'interface OrderedMapIterator permet de parcourir une Map ordonnée et définit
les méthodes hasPrevious et previous dont les noms sont explicites.
Les deux implémentations de référence permettant d'utiliser ces itérateurs sont respectivement
org.apache.commons.collections.HashedMap et org.apache.commons.collections.LinkedMap. La classe
LinkedMap maintient les éléments dans l'ordre d'insertion.
Différentes implémentations répondant à des besoins particuliers sont également fournies :
En plus des décorateurs communs présentés au début de cet article, les commons collections proposent des
décorateurs spécifiques aux Maps :
L'interface MultiMap permet de faire correspondre plusieurs valeurs à une même clé.
Les clés renvoient en fait à des collections auxquelles sont ajoutées les valeurs lors de l'appel de put.
L'implémentation de référence de la MultiMap est la classe MultiHashMap.
X. Set
L'API commons collections ne définit pas de nouvelle interface pour les Set mais offre divers décorateurs,
définis dans la package org.apache.commons.collections.set :
XI. Autres
Il existe encore d'autres "familles" d'entités qui ajoutent des fonctionnalités à différentes catégories de collections.
Nous en présentons ici quelques unes.
FastArrayList, FastHashMap, FastTreeMap sont destinées à être utilisées dans des
environnements multi-threads où les opérations de lecture sont beaucoup plus nombreuses que celles
de modifications. Lors de l'instanciation, la collection est en mode slow, c'est-à-dire que toutes
les méthodes sont synchronisées. Lorsque la collection a été remplie, il est préférable de basculer
en mode fast. Dans ce mode, les accès qui ne modifient pas la collection ne sont pas synchronisés.
Lors d'un accès qui modifie la collection, celle-ci est clonée, la modification est effectuée
sur le clone, puis c'est ce clone qui est utilisé.
XII. Remerciements
Merci à vedaer pour sa relecture.
XIII. Téléchargements
|