Les collections. Les tableaux pour gérer des ensembles d'éléments. Chapitres traités

Dimension: px
Commencer à balayer dès la page:

Download "Les collections. Les tableaux pour gérer des ensembles d'éléments. Chapitres traités"

Transcription

1 Les collections Chapitres traités Les tableaux pour gérer des ensembles d'éléments Nous allons ici faire l'étude du regroupement mémoire d'éléments de même type que nous appelons collections. Nous en profiterons pour revoir les tableaux classiques pour mieux cerner l'intérêt de proposer d'autres alternatives. Les collections que vous choisirez peut engendrer une grande différence, à la fois au moment de l'implémentation des méthodes et en termes de performances. Faut-il rechercher rapidement parmi des miliers (voire des millions) d'éléments triés? Devez-vous rapidement insérer et supprimer des éléments au milieu d'une suite triée? Devez-vous établir des associations entre clés et valeurs? Cette étude met en évidence la façon dont la bibliothèque Java peut vous aider à trouver une structure de données adaptée à une programmation sérieuse, et par là, choisir la collection équivalente. Attention : afin de bien maîtriser les différents concepts mis en oeuvre sur ces collections, il est important de connaître la généricité qui est expliquée dans l'étude précédente. Les tableaux pour gérer des ensembles d'éléments Un tableau (array en anglais) est le type d'objet le plus simple à programmer pour gérer un ensemble d'éléments : 1. Les éléments d'un tableau sont tous du même type. 2. Ces éléments peuvent être des données de type primitif ou des références d'une classe donnée. 3. La taille d'un tableau est déterminée lors de sa création et ne peut être modifiée par la suite. 4. Le tableau est considéré comme un objet et possède un attribut public length qui donne la taille du tableau. 5. La position d'un élément dans un tableau est déterminée par un indice entier. 6. L'indice du prmier élément du tableau est toujours 0 et l'indice du dernier élément d'un tableau est le nombre d'éléments moins La JVM vérifie à l'exécution que les indices utilisés pour accéder à un élément figurent bien dans l'intervalle autorisé et que le type d'un objet est compatible avec le type du tableau. Tableau java Un tableau java est un objet qui mémorise un ensemble de valeurs contiguës en mémoire, auxquelles on accède grâce à un indice entier compris entre 0 et le nombre d'éléments moins un. Un tableau qui contient des valeurs de type primitif, comme le tableau nombrepremier, stocke directement ses valeurs les unes à la suite des autres, tandis que celui qui contient des objets, comme le tableau smileys, stocke les références sur ses objets. Les tableaux permettent donc de regrouper une suite de variables de même type. Chaque élément du tableau est une variable que vous pouvez utiliser comme n'importe quelle variable de ce type. Il est possible de définir des tableaux pour les types primaires ou les classes. Cependant, tous les éléments doivent être du même type. En Java, les tableaux sont des objets. Pour pouvoir utiliser un tableau, vous devez respecter les trois étapes suivantes : 1. Déclarer une variable pour référencer le tableau. 2. Créer un objet de type tableau en initialisant la référence à ce tableau. 3. Utiliser et manipuler les éléments de ce tableau. Déclaration d'un tableau Effectivement, comme un tableau est un objet, la création d'un tableau commence par la mise en place de sa référence afin de pointer ultérieurement vers l'objet tableau lui-même. Voici comment se déclare une telle référence t de type tableau d'entiers : int t[]; Elle précise que t est destiné à contenir la référence à un tableau d'entiers. Vous constatez qu'aucune dimension ne figure dans cette déclaration et, pour l'instant, aucune valeur n'a été attribuée à t. Comme toute référence, une référence de type tableau peut être égale à null ou désigner un objet tableau. Voici quelques exemples de déclarations : int t[]; // peut s'écrire int[] t ; int [] t1, t2; // t1 et t2 sont des références à des tableaux d'entiers int t3[], t4[]; // écritures équivalentes int t5[], n, t6[]; // t5 et t6 sont des tableaux d'entiers, n est entier Point tp[]; // tp est une référence à un tableau d'objets de type Point Point a, tp[]; // a est une référence à un objet de type Point, tp est une référence à un tableau d'objets de type Point int tab[5]; // erreur : nous ne pouvons pas indiquer de dimension ici. Création et initialisation d'un tableau Nous pouvons effectuer la création et l'initialisation d'un tableau de trois façons différentes : Création par l'opérateur new Après avoir déclaré la variable tableau, il faut allouer les éléments de ce tableau. On utilise pour ce faire l'opérateur new. Vous avez déjà mis en oeuvre cet opérateur pour créer des objets. A ce niveau, il est obligatoire de préciser le nombre d'éléments du tableau. Ces derniers sont initialisés automatiquement en fonction du type de tableau (0 pour les numériques, '\0' pour les caractères, false pour les boolean et null pour les éléments de type objet). Position des crochets Java laisse la liberté de placer les crochets [] avant ou après l'identificateur de tableau : int t[] ; ou bien int[] t ; Attention : Tableau d'objets (de références) La création d'un tableau de type objet ne génère pas d'objet pour chaque élément, mais uniquement des références initialisées à null par défaut. Création du tableau avec new On crée un tableau comme on crée un objet, c'est à dire en utilisant l'opérateur new. On précise à la fois le type des éléments, ainsi que leur nombre (dimension du tableau). t = new int[5]; // t fait référence à un tableau de 5 entiers Cette instruction alloue l'emplacement nécessaire à un tableau de 5 éléments de type int et place la référence dans t. Les 5 éléments sont initialisés par défaut (comme tous les champs d'un objet) à une valeur nulle (0 pour int). package test; import java.awt.*; public class Main { public static void main(string[] args) { int[] tabentier = new int[30]; // tableau de 30 entiers de valeur nulle String[] semaine = new String[7]; /* tableau de 7 références vers des objets * de type String. Par la suite, il sera nécessaire * pour chaque élément du tableau de * créer un objet de type String Autres exemples de créations float[] tabnombres = new float[5]; String[] textes;...

2 */ Utilisation d'un initialiseur Lors de la déclaration d'une référence, il est possible de fournir la valeur à chacune des cases du tableau. Il suffit de donner la liste entre accolades sans utiliser l'opérateur new. Java se sert du nombre de valeurs figurant dans l'initialiseur pour en déduire la taille du tableau à créer. int taille = 10; textes = new String[taille]; Initialisation avec une liste de valeurs int[] nombrepremiers = {1, 2, 5, 7, 11, 13; String[] smileys = {";)", ":(", ":o"; int[] t = {1, 3, 5, 7; /* création d'un tableau de 4 entiers de * valeurs respectives 1, 3, 5, 7 et place * la référence dans t. Elle remplace les * instructions suivantes : */ int t[]; t = new int[4]; t[0]=1; t[1]=3; t[2]=5; t[3]=7; tableau anonyme A tout moment en utilisant l'opérateur new et une liste entre accolades de valeurs. Cette notation est très pratique pour passer un tableau en paramètres à une méthode sans qu'il faille créer une variable locale pour ce tableau. En imaginant la déclaration de la méthode suivante : affichesemaine(string[] jourssemaine); Voici un exemple de tableau anonyme que nous pouvons passer en argument de la méthode : affichesemaine(new String[] {"Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche"); Cette écriture est à la fois un raccourci et évite d'utiliser une variable intermédiaire : String[] jours = {"Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche" ; affichesemaine(jours); Utilisation d'un tableau Nous pouvons manipuler un élément de tableau comme nous le ferions avec n'importe quelle variable ou n'importe quel objet de ses éléments. On désigne un élément particulier en plaçant entre crochets, à la suite du nom du tableau, une expression entière nommée indice indiquant sa position. Le premier élément correspond à l'indice 0 (et non 1). int t[] = new int[5], a; t[0] = 15; // place la valeur 15 dans le premier élément du tableau t[2]++; // incrémente de 1 le troisième élément de t a = t[4]+23; // utilisation dans un calcul t[1] = t[0]+5; // initialisation d'un élément à partir d'un autre t[a-20] = -85; // calcul sur l'indice Affectation de tableaux Java permet aussi de manipuler globalement des tableaux, par le biais d'affectations de leurs références. java.util.arrays La classe java.util.arrays contient un ensemble de méthodes de classe qui permettent d'effectuer des traitements sur les tableaux de type primitif ou de type d'objet : tostring(type[] tableau) : renvoie une chaîne de caractères avec les éléments de tableau, entre parenthèses et délimités par des virgules. deeptostring(type[] tableau) : renvoie une chaîne de caractères avec les éléments de tableau dont la structure est par nature multidimensionnel. equals() : compage les éléments de deux tableaux ; fill() : remplit tout ou partie d'un tableau avec une valeur donnée ; sort() : trie les éléments d'un tableau dans l'ordre ascendant ; Exécutons maintenant l'affectation : t1 = t2; // la référence contenue dans t2 est recopiée dans t1 Nous aboutissons à la situation présentée ci-dessous. Dorénavant, t1 et t2 désigne le même tableau. binarysearch() : renvoie l'indice du premier élément égal à une valeur dans un tableau trié. import java.util.arrays; public class Main { public static void main(string[] args) { String[] liste = {"Salut", "Bonjour"; String[] copie = new String[2]; System.arraycopy(liste, 0, copie, 0, 2); Arrays.sort(liste); boolean test = Arrays.equals(copie, liste); Nouveauté sur la boucle for (for each) Le JDK 5.0 a introduit une construction de boucle performante qui vous permet de parcourir chaque élément d'un tableau (ainsi que d'autres collections d'éléments) sans avoir à vous préoccuper des valeurs d'indice. Ainsi, avec : La boucle for améliorée :

3 t1[1] = 5; System.out.println (t2[1]); on obtiendra l'affichage de la valeur 5, et non 11. Si l'objet que constitue le tableau de trois entiers anciennement désigné par t1 n'est plus référencé par ailleurs, il deviendra candidat au ramasse-miettes. Il est très important de noter que l'affectation de références de tableaux n'entraine aucune recopie des valeurs des éléments du tableau. On retrouve exactement le même phénomène que pour l'affectation d'objets. Nombre d'éléments d'un tableau Pour connaître le nombre d'éléments d'un tableau, vous pouvez utiliser l'attribut public length. Cet attribut peut être utilisé pour tous les types de tableaux. Considérez le code suivant : int tab[] = new int [10]; int taille = tab.length; // taille est égal à 10 Après avoir déclaré un tableau, utilisez length pour récupérer le nombre d'éléments du tableau (ici 10). Le mot length désigne en quelque sorte un attribut du tableau, attribut que l'on peut d'ailleurs uniquement lire ; ce n'est pas un attribut au même titre que les attributs d'une classe. Remarquons qu'il n'y a pas d'autre attribut pour une variable de type tableau. Manipuler un tableau d'objets Attention : Pour définir un tableau d'objets, il faut commencer par définir un tableau de références, puis, pour chaque référence, créer l'objet associé. Un tableau d'objets est en fait un tableau de références, et chaque référence pointe vers l'objet associé. for (variable : collection) instruction définit la variable donnée sur chaque élément de la collection, puis exécute l'instruction (qui, bien sûr, peut être un bloc). L'expression collection doit être un tableau ou un objet d'une classe qui implémente l'interface Iterable, comme ArrayList. Par exemple : int[] tableau = new int[10]; for (int élément : tableau) System.out.println(élément); // affiche chaque élément du tableau sur une ligne séparée. Il est conseillé de lire cette boucle sous la forme "pour chaque élément dans tableau". Bien entendu, vous pourriez obtenir le même effet avec la boucle for traditionnelle : int[] tableau = new int[10]; for (int élément=0; élément<10; élément++) System.out.println(tableau[élément]); Toutefois, la boucle "for each" est plus concise et moins sujette à erreur (vous n'avez pas à vous inquiéter des valeurs d'indice de début et de fin, qui sont souvent pénibles). La variable loop de la boucle "for each" parcourt les éléments d'un tableau, et non les valeurs d'indice. La boucle "for each" est une amélioration agréable de la boucle traditionnelle si vous devez traiter tous les éléments d'une collection. Il y a toutefois de nombreuses opportunités d'utiliser la boucle for traditionnelle. Vous ne voudrez pas, par exemple, parcourir la totalité de la collection ou pourriez avoir besoin de la valeur d'indice à l'intérieur de la boucle. Le résultat est le suivant : Point : 1, 2 Point : 4, 5 Point : 8, 9 Il est possible d'avoir une écriture plus concise de la classe TabPoint en utilisant l'initialiseur de tableau. Afficher un tableau dans la sortie standard Il existe une méthode très très simple pour afficher toutes les valeurs d'un tableau dans la sortie standard, et ce grâce à la méthode tostring() de la classe Arrays. L'appel Arrays.toString(tableau) renvoie une chaîne de caractères contenant les éléments du tableau, insérés entre crochets et séparés par des virgules : System.out.println(Arrays.toString(tableau)); // affichepar exemple à l'écran "[2, 3, 5, 7, 11, 13]" Copie des tableaux Il est possible de copier une variable tableau dans une autre, mais attention, comme nous l'avons vu plus haut, les deux variables feront alors référence au même tableau : int[ ] suite = {17, 19, 23, 29, 31, 37; int[ ] entier = suite; entier[3] = 12; // suite[3] vaut maintenant 12 Si vous voulez effectivement copier toutes les valeurs d'un tableau dans un autre, il faut employer la méthode copyto() de la classe Arrays : int[ ] suite = {17, 19, 23, 29, 31, 37; int[ ] entier = Arrays.copyOf(suite, suite.length); Le second paramètre correspond à la longueur du nouveau tableau. Cette méthode est souvent employée pour augmenter la taille d'un tableau : int[ ] suite = {17, 19, 23, 29, 31, 37; suite = Arrays.copyOf(suite, 2 * suite.length); Les autres éléments sont remplis de 0 si le tableau contient des nombres, false si le tableau contient des valeurs booléennes. A l'inverse, si la longueur est inférieure à celle du tableau initial, seules les valeurs initiales sont copiées. Tri d'un tableau

4 Si vous voulez trier un tableau de nombres, utilisez une des méthodes sort() de la classe Arrays : int[ ] nombre = new int[100];... Arrays.sort(nombre); Cette méthode utilise une version adaptée de l'algorithme QuickSort qui se révèle très efficace sur la plupart des ensembles de données. Parcourt et affichage d'un tableau multidimentionnel Une boucle "for each" ne traverse pas automatiquement toutes les entrées d'un tableau multidimensionnel. Elle parcourt plutôt les lignes, qui sont elles-mêmes des tableaux à une dimension. Pour visiter tous les éléments d'un tableau bidimensionnel, imbriquer deux boucles : int[ ][ ] tableau = { {16, 3, 2, 13, {5, 10, 11, 8, {9, 6, 7, 12, {4, 15, 14, 1 ;... for (int[ ] ligne : tableau) for (int élément : ligne) // faire quelque chose avec élément Pour afficher une liste rapide des éléments d'un tableau bidimensionnel, utilisez la méthode deeptostring() de la classe Arrays : System.out.println(Arrays.deepToString(tableau)); [[16, 3, 2, 13], [5, 10, 11, 8], [9, 6, 7, 12], [4, 15, 14, 1]] Affichage correspondant Les collections pour gérer des ensembles d'objets Les tableaux sont inadéquats pour gérer une quantité importante d'informations du même type quand leur nombre n'est pas connu à l'avance. Par exemple, le nombre de messages postés dans un forum n'étant pas limité, il existe des solutions plus simples que des tableaux pour stocker ces messages. Le paquetage java.util contient plusieurs classes de collection utilisées pour gérer un ensemble d'éléments de type d'objet et résoudre les limitations inhérentes aux tableaux, ce que nous allons aborder dans les chapitres qui suivent. Les interfaces de collection Comme pour la plupart des bibliothèques de données récentes, la bibliothèque de collections Java fait la distinction entre les interfaces et les implémentations. Intéressonsnous à cette distinction avec une structure de données familière, la queue de données. Interface de queue de données Une interface de queue indique que vous pouvez : 1. ajouter des éléments à la fin d'une queue, 2. supprimer des éléments au début d'une queue, 3. et déterminer le nombre d'éléments contenus dans une queue. Les queues sont utilisées si vous devez enregistrer des objets et les récupérer selon la règle "premier entré, premier sortie" (FIFO - First In First Out) Une forme minimaliste d'interface pour les queues pourrait ressembler à ceci : interface Queue<E> { void add(e élément); E remove(); int size(); L'interface n'indique en aucune manière la façon dont la queue est implémentée. En réalité, il existe deux implémentations courantes de queues :

5 1. La première implémentation, représentée par la classe ArrayDeque, se sert d'un tableau circulaire : 2. La seconde implémentaiton, représentée par la classe LinkedList, se sert d'une liste chaînée : Lorsque vous vous servez d'une queue dans vos programmes, vous n'avez pas besoin de connaître l'implémentation réellement utilisée une fois que la collection est construite. Par conséquent, il est logique d'avoir recours à une classe existante uniquement lorsque vous construisez l'objet collection. Le type d'interface permet ensuite de faire référence à la collection. Queue<String> textes = new ArrayDeque<String>(); textes.add("premier texte"); Avec cette approche, si vous changez de point de vue, vous pouvez facilement utiliser une implémentation différente. Vous n'avez besoin de modifier votre programme qu'à un seul endroit : le constructeur. Si vous décidez qu'après tout, une LinkedList constitue un meilleurs choix, votre programme devient : Queue<String> textes = new LinkedList<String>(); textes.add("premier texte"); Pourquoi choisir une implémentation plutôt qu'une autre? Une interface ne fournit aucun détail quant à l'efficacité de son implémentation. De manière générale, un tableau circulaire est plus efficace qu'une liste chaînée, et il est donc préférable à cette dernière. Toutefois, comme d'habitude, il y a un prix à payer. Les tableaux circulaires font partie d'une collection bornée, qui a une capacité déterminée. Si vous ne connaissez pas la limite maximale du nombre d'objets stockés par votre programme, vous préférerez probablement une implémentation en liste chaînée. En étudiant la documentation de l'api, vous découvrirez un autre jeu de classes dont le nom commence par Abstract, comme AbstractQueue. Ces classes sont destinées aux implémentations de bibliothèque. Pour implémenter votre propre classe de queue, il sera plus facile d'étendre AbstractQueue que d'implémenter toutes les méthodes de l'interface Queue. Interface de collection En réalité, l'interface fondamentale des classes de collection de la bibliothèque Java est l'interface Collection. Cette interface possède deux méthode essentielles (il existe d'autres méthodes que nous aborderons plus loin dans ce chapitre) : public interface Collection<E> { boolean add(e élément); Iterator<E> iterator();... La méthode add() ajoute un élément dans la collection. La méthode add() renvoie true si l'ajout de l'élément a effectivement modifié la collection. Elle renvoie false si la collection n'a pas été modifiée. Par exemple, si vous essayez d'ajouter un objet dans un ensemble et que cet objet en fasse déjà partie, la requête add() est sans effet parce que les ensembles de données ne peuvent pas accepter deux fois la même donnée. La méthode iterator() renvoie un objet qui implémente l'interface Iterator. Un objet d'itération permet de parcourir les différents éléments d'une collection, un par un. L'interface Iterator Les itérateurs permettent de parcourir la collection d'un début vers une fin en ne passant qu'une seule fois sur chacun des éléments. Associé à une collection donnée, l'itérateur possède les propriétés suivantes : 1. Â un instant donné, un itérateur indique ce que nous nommons une position courante désignant soit un élément donné de la collection, soit la fin de la collection (la position courante se trouvant alors en quelque sorte après le dernier élément). Comme nous pouvons nous y attendre, le premier appel de la méthode iterator() sur une collection donnée fournit comme position courante, le début de la collection. 2. Nous pouvons obtenir l'objet désigné par un itérateur en appelant la méthode next() de l'itérateur, ce qui, en outre, avance l'itérateur d'une position. Ainsi deux appels successifs de next() fournissent deux objets différents (consécutifs).

6 3. La méthode hasnext() de l'itérateur permet de savoir si l'itérateur est ou non en fin de collection, c'est-à-dire si la position courante dispose ou non d'une position suivante, autrement dit si la postion courante désigne ou non un élément. Les itérateurs implémentent l'interface Iterator qui possède trois méthodes dont nous venons de découvrir les deux principales : public interface Iterator<E> { E next(); boolean hasnext(); void remove(); Comme nous l'avons évoqué plus haut, en appelant plusieurs fois la méthode next(), vous pouvez parcourir tous les éléments d'une collection un par un. Lorsque la fin de la collection est atteinte, la méthode next() déclenche une exception NuSuchElementException. Par conséquent, il convient d'appeler la méthode hasnext() avant la méthode next(). Cette méthode renvoie true si l'objet de l'itération possède encore au moins un élément. 1. Par conséquent, si vous souhaitez parcourir tous les éléments d'un conteneur, il suffit de demander un objet Iterator et d'appeler la méthode next() tant que la méthode hasnext() renvoie true. Par exemple : Collection<String> textes =...; Iterator<String> it = textes.iterator(); while (it.hasnext()) { String élément = it.next(); // utilisation de élément 2. Heureusement, depuis Java SE 5.0, nous pouvons utiliser un raccourci élégant et bien plus concis au moyen de la boucle "foreach" : Collection<String> textes =...; for (String élément : textes) { // utilisation de élément Le compilateur se contente de traduire la boucle "for each" en boucle avec un itérateur. La boucle "for each" fonctionne comme tout objet qui implémente l'interface Iterable, une interface avec une seule méthode, la méthode iterator() : public interface Iterable<E> { Iterator<E> iterator(); L'interface Iterable L'interface Collection étend l'interface Iterable. Vous pouvez donc utiliser la boucle "for each" avec n'importe quelle collection de la bibliothèque standard. Retour à l'interface Collection L'ordre de visite des éléments dépend du type de la collection. Si vous parcourez un ArrayList, l'itérateur démarre à l'indice 0 et l'incrémente à chaque pas. Toutefois, si vous visitez les éléments dans un HashSet, vous les rencontrerez dans un ordre aléatoire. Vous pouvez être sûr de rencontrer tous les éléments d'un collection lors de l'itération, mais vous ne pouvez pas savoir dans quel ordre. Cela ne pose généralement pas de problème car l'ordre importe peu pour des calculs comme les totaux ou les concordances. Suppression d'éléments à l'aide de l'interface Iterator L'interface Iterator prévoit la méthode remove() qui supprime de la collection le dernier objet envoyé par la méthode next(). Dans de nombreux cas, cette méthode est pratique si vous avez besoin d'examiner chaque élément avant de savoir si vous devez le supprimer ou non. Mais, si vous souhaitez effacer un élément en fonction de sa position, il faut malgré tout le dépasser. Par exemple, voici comment supprimer le premier élément d'une collection de chaînes : Iterator<String> it = textes.iterator(); it.next(); it.remove(); Notez bien que la méthode remove() ne travaille pas directement avec la position courante de l'itérateur, mais avec la dernière référence renvoyée par la méthode next() qui s'appelle objet courant. Il existe en effet une relation entre les appels aux méthodes next() et remove(). Il n'est pas permis d'appeler remove() sans avoir au préalable appelé next(). Si vous essayez, une IllegalStateException sera déclenchée. Bien que l'itérateur soit placé en début de collection, il n'existe encore aucun élément courant car aucun objet n'a encore été renvoyé par la méthode next(). Ainsi, pour supprimer le premier objet de la collection, il faut d'abord l'avoir lu. Donc, si vous désirez supprimer deux éléments successifs, vous n'avez pas le droit d'appeler : it.remove(); it.remove(); // erreur Il faut appeler next() pour passer au-dessus de l'élément à supprimer : it.remove(); it.next(); it.remove(); // Ok On notrera bien que l'interface Iterator ne comporte pas de méthode d'ajout d'un élément à une position déterminée (c'est-à-dire en général entre deux éléments). En effet, un tel ajout n'est réalisable que sur des collections disposant d'informations permettant de localiser, non seulement l'élément suivant, mais aussi l'élément précédent d'un élément donné. Ce ne sera le cas que de certaines collections seulement, disposant précisément d'itérateurs bidirectionnels.

7 En revanche, nous verrons que toute collection disposera d'une méthode d'ajout d'un élément à un emplacement (souvent sa fin) indépendant de la valeur d'un quelconque itérateur. C'est d'ailleurs cette démarche qui sera le plus souvent utilisée pour créer effectivement une collection. Les itérateurs bidirectionnels : l'interface ListIterator Certaines collections (listes chaînées, vecteurs dynamiques) peuvent, par nature, être parcourues dans les deux sens. Elles disposent d'une méthode nommée listiterator() qui fournit un itérateur bidirectionnel. Il s'agit, cette fois, d'un objet d'un type implémentant l'interface ListIterator<E> (qui dérive de l'interface Iterator<E>). Il dispose bien sûr des méthodes next(), hasnext() et remove() héritées de Iterator. Mais il dispose d'autres méthodes permettant d'exploiter son caractère bidirectionnel, à savoir : 1. Comme nous pouvons nous y attendre, des méthodes previous() et hasprevious(), complémentaires des méthodes next() et hasnext() : Nous pouvons obtenir l'élément précédent la position courante à l'aide de la méthode previous() de l'iterateur, laquelle, en outre, recule l'itérateur sur la position précédente. Ainsi, deux appels successifs de previous() fournissent deux objets différents. La méthode hasprevious() de l'itérateur permet de savoir si nous sommes ou non en début de collection, c'est-à-dire si la position courante dispose ou non d'une position précédente. LinkedList<String> textes =...; ListIterator<String> it = textes.listiterator(texte.size()); while (it.hasprevious()) { String élément = it.previous(); // utilisation de élément Un appel à la méthode previous() annule, en quelque sorte, l'action réalisée sur le pointeur de déplacement, par un précédent appel à la méthode next(). 2. Mais aussi, des méthodes d'addition d'un élément à la position courante - add() (en générale, une telle opération est plutôt nommée insertion, mais ici, la méthode se nomme add() et non insert())- ou de modification de l'élément courant - set(). L'interface ListIterator prévoir une méthode add() qui ajoute un élément à la position courante de l'itérateur. Si ce dernier est en fin de collection, l'ajout se fait tout naturellement en fin de collection (y compris si la collection est vide). Si l'itérateur désigne le premier élément, l'ajout se fera avant ce premier élément. Par exemple, si textes est une collection disposant d'un itérateur bidirectionnel, les instructions suivantes ajouterons l'élément avant le deuxième élément (en supposant qu'il existe) : LinkedList<String> textes =...; ListIterator<String> it = textes.listiterator(texte.size()); it.next(); it.next(); it.add(élément); Nous noterons bien ici, quelle que soit la position courante, l'ajout par add() est toujours possible. De plus, contrairement à remove(), cet ajout ne nécessite pas que l'élément courant soit défini (il n'est pas nécessaire qu'un quelconque élément ait déjà été renvoyé par next() ou previous()). Par ailleurs, add() déplace la position courante après l'élément que nous venons d'ajouter. Plusieurs appels consécutifs de add() sans intervention explicite sur l'itérateur introduisent donc des éléments consécutifs. L'appel à la méthode set(élément) remplace l'élément courant, c'est-à-dire le dernier renvoyé par next() et previous(), à condition que la collection n'ait pas été modifiée entre temps (par exemple par add() ou remove()). N'oubliez pas que les éléments ne sont que de simples références ; la modification opérée par set() n'est donc qu'une simple modification de référence (les objets concernés n'étant pas modifiés). La position courante de l'itérateur n'est pas modifiée (plusieurs appels successifs de set(), sans action sur l'itérateur, reviennent à ne retenir que la dernière modification). N'oubliez pas que set(), comme remove(), s'applique à un élément courant (et non comme add() à une position courante). Méthodes utilitaires génériques de l'interface Collection Comme les interfaces Iterator et Collection sont générales, il est possible d'écrire des méthodes pratiques pouvant travailler sur n'importe quel type de collection. Par exemple, voici une méthode générale qui teste si une collection arbitraire contient un élément donné : public static<e> boolean contains(collection<e> collection, Object objet) for (E élément : collection) if (élément.eguals(objet)) return true; return false; Les concepteurs de la bibliothèque Java ont décidé que certaines de ces méthodes générales étaient tellement pratiques qu'elles devaient faire partie de la bibliothèque. De cette façon, les utilisateurs n'ont pas besoin de réinventer la roue en permanence. La méthode contains() en constitue un bon exemple. Collection<E> iterator() Renvoie un itérateur pouvant être utilisé pour parcourir les éléments d'une collection int size() Renvoie le nombre courant d'éléments stockés dans une collection. boolean isempty() Renvoie true si la collection ne contient aucun élément. boolean contains(object objet) Renvoie true si la collection contient l'objet correspondant à celui qui est passé en argument. boolean containsall(collection<?> autre) Renvoie true si cette collection contient tous les éléments de l'autre collection. boolean add(object élément) Ajoute un élément à la fin de la collection. Renvoie true si la collection a été modifié par cet appel. L'interface java.util.collection<e>

8 boolean addall(collection<? extends E> autre) Ajoute tous les éléments de l'autre collection à cette collection. Renvoie true si la collection a été modifié par cet appel. boolean remove(object élément) Supprime cet élément de la collection. Renvoie true si l'objet correspondant a été trouvé. boolean removeall(collection<?> autre) Supprime de cette collection tous les éléments de l'autre collection. Renvoie true si la collection a été modifié par cet appel. Handler[] gethandlers() Récupère tous les gestionnaires de cet enregistreur. void clear() Supprime tous les éléments de la collection. boolean retainall(collection<?> autre) Supprime dans cette collection tous les éléments qui ne figure pas dans l'autre collection. Renvoie true si la collection a été modifié par cet appel. Object[] toarray() Renvoie un tableau contenant tous les objets de la collection. boolean hasnext() Renvoie true s'il reste un élément à parcourir. E next() Renvoie le prochain objet à parcourir. Déclenche une exception NoSuchElementException si la fin de la collection est atteinte. void remove() L'interface java.util.iterator<e> Supprime le dernier objet lu. Cette méthode doit impérativement être appelée après une lecture. Si la collection a été modifiée depuis la dernière lecture, cette méthode renvoie une IllegalStateException. void add(e élément) Ajoute (insère) un élément avant la position courante. void set(e élément) L'interface java.util.listiterator<e> qui hérite de l'interface java.util.iterator<e> Remplace le dernier élément renvoyé par next() ou previous() par un nouvel élément. Déclenche une IllegalStateException si la structure de la liste a été modifiée depuis le dernier appel à next() ou à previous(). boolean hasprevious() Renvoie true s'il existe un élément avant la position courante.. E previous() Renvoie l'objet précédent. Déclenche une exception NoSuchElementException lorsque le début de la liste est atteinte. int nextindex() Renvoie l'indice de l'élément qui serait renvoyé par le prochain appel à next(). int previousindex() Renvoie l'indice de l'élément qui serait renvoyé par le prochain appel à previous(). Comme il est plutôt fastidieux d'ajouter toutes ces méthodes dans chaque classe implémentant l'interface Collection, la bibliothèque fournit la classe AbstractCollection, qui définit les méthodes fondamentales (telles size() et iterator()) comme des méthodes abstraites et implémente le corps des méthodes utilitaires génériques à leur place. Par exemple : public abstract class AbstractCollection<E> implements Collection<E> {... public abstract Iterator<E> iterator();... public static<e> boolean contains(collection<e> collection, Object objet) for (E élément : collection) if (élément.eguals(objet)) return true; return false; Une classe de collection concrète peut maintenant étendre la classe AbstractCollection. C'est donc à la classe de collection concrète de fournir une méthode iterator(), mais la méthode contains() est gérée par la super-classe AbstractCollection. De plus, si la sous-classe propose une meilleure implémentation de la méthode contains(), elle est libre de l'utiliser. Il s'agit là d'une bonne architecture pour un ensemble de classes. Les utilisateurs des classes de collection disposent ainsi d'un ensemble de méthodes très complet disponible au travers de l'interface générale et les programmeurs chargés des structures de données n'ont pas besoin de se préoccuper de toutes les méthodes. Les collections concrètes Maintenant que nous avons pris connaissance avec les interfaces principales pour gérer les collections et plutôt que de voir en détail chaque interface, il est plus utile dès lors de commencer par aborder les structures de données concrètes implémentées dans la bibliothèque Java. Une fois que nous aurons correctement décrit les classes que nous pouvons utiliser, nous reviendrons à des considérations abstraites et nous verrons comment la structure des collections organise toutes ces classes. Le tableau ci-dessous présente les collections de la bibliothèque Java et décrit brièvement l'objectif de chaque classe de collection (pour des raisons de simplicité, nous ne parlrons pas des collections compatibles avec les Threads qui seront traitées dans une autre étude). Toutes les classes du tableau implémente l'interface Collection, à l'exception de celles dont le nom se termine par Map. Celles-ci implémentent plutôt l'interface Map (nous la traiterons un peu plus loin).

9 Type de collection ArrayList LinkedList ArrayDeQue HashSet TreeSet EnumSet LinkedHashSet PriorityQueue HashMap TreeMap EnumMap LinkedHashMap WeakHashMap IdentityHashMap Description Une séquence indexée qui grandit et se réduit de manière dynamique. Une séquence ordonnée qui permet des insertions et des retraits effectifs à n'importe quel endroit. Une queue à deux extrémités implémentée sous forme de tableau circulaire. Un ensemble de valeur non ordonnée qui refuse les réplications (comme dans la théorie des ensembles). Un ensemble trié. Un ensemble de valeur de type énuméré. Un ensemble qui se souvient de l'ordre d'insertion des éléments. Une collection qui permet un retrait effectif de l'élément le plus petit. Une structure de données qui stocke les associations clé/valeur. Une concordance dans laquelle les clés sont triées. Une concordance dans laquelle les clés appartiennent à un type énuméré. Une concordance qui se souvient de l'ordre d'ajout des entrées. Une concordance avec des valeurs pouvant être réclamées par le ramasse-miettes si elles ne sont pas utilisées ailleurs. Une concordance avec des clés comparées par ==, et non par equals() Listes chaînées - java.util.linkedlist Nous avons déjà abordé les tableaux et, dans de nombreux exemples, leur cousin dynamique, la classe ArrayList (qui sera abordée dans le chapitre suivant). Voici recensés ci-dessous les différents critères relatifs à leur structure : 1. Le tableau classique en Java, comme nous l'avons découvert au début de cette étude, fait parti des collections d'éléments ordonnés accessible par indice. L'utilisation des tableaux impose cependant de spécifier une taille avant de pouvoir être utilisé. En java, il est possible de préciser la taille juste au moment où nous en avons besoin. En contre partie, une fois que la taille est prise en compte, il n'est pas facile de la changer. 2. La classe ArrayList est semblable à un tableau, mais elle ajuste automatiquement sa capacité à mesure que vous ajoutez et supprimez des éléments, sans que vous n'ayez rien à faire. En fait, en interne, la classe ArrayList mémorise ses éléments dans un tableau Java. Si ce tableau interne est trop petit lors de l'ajout d'un nouvel élément à la collection, il est automatiquement remplacé par un nouveau tableau, plus grand, initialisé avec les références de l'ancien tableau. La classe ArrayList est très séduisante. Il s'agit ni plus ni moins d'un tableau dynamique. Elle est totalement adaptée lorsque nous devons ajouter ou supprimer des éléments en fin de tableau. Mais ces deux structures possèdent un inconvénient majeur. La suppression d'un élément au milieu d'un tableau nécessite beaucoup de temps machine, car tous les éléments situés après l'élément supprimé doivent être décalés d'une case. Le même problème se pose pour insérer des éléments au milieu d'un tableau. Il existe une autre structure de données très répandue, la liste chaînée, qui permet de résoudre ces problèmes. Alors que les objets d'un tableau occupent des emplacements mémoires successifs, une liste chaînée stocke chaque objet avec un lien qui y fait référence. Chaque lien possède également une référence vers le lien suivant de la liste. Avec Java, chaque élément d'une liste chaînée possède en fait deux liens, c'est-à-dire que chaque élément est aussi relié à l'élément précédent. La bibliothèque Java possède la classe LinkedList prète à l'emploi et qui implémente donc la liste "doublement chaînée". La classe LinkedList mémorise ses éléments avec une liste doublement chaînée, ce qui permet d'insérer plus rapidement un élément dans la collection, mais par contre, ralentit l'accès à un élément par son indice. Il existe une différence entre les listes chaînées et les collections génériques. Une liste chaînée est en effet une collection classée dans laquelle la position des objets a une importance. La méthode add() de LinkedList ajoute un objet à la fin de la liste. Mais vous aurez souvent besoin d'ajouter un élément quelque part au

10 milieu d'une liste. Cette méthode add(), qui dépend de la postion courante, dépend aussi d'un itérateur, car ce sont ces itérateurs qui sont chargés de stocker les positions dans une collection. L'emploi d'un itérateur pour ajouter des éléments n'a un sens que si les collections concernées sont classées selon un ordre implicite. Par exemple, le type de données "set" que nous aborderons dans un prochain chapitre n'impose aucun ordre à ses éléments. C'est pourquoi il n'existe aucune méthode add() dans l'interface Iterator. Pour compenser, la bibliothèque de collections fournit une sous-interface de Iterator, que nous avons déjà découvert, qui se nomme ListIterator et qui possède effectivement la méthode add(). Contrairement à la méthode add() de Collection, cette méthode add() de ListIterator ne renvoie par un boolean, c'est-à-dire que l'opération add() est censée toujours modifier la liste. La classe LinkedList permet ainsi de manipuler des listes doublement chaînées. A chaque élément de la collection, on associe (de façon totalement transparente pour le programmeur) deux informations supplémentaires qui ne sont autres que les références à l'élément précédent et au suivant. Une telle collection peut être ainsi parcourue à l'aide d'un itérateur bidirectionnel de type ListIterator. Il existe une méthode listiterator() de la classe LinkedList qui renvoie un objet itérateur qui implémente l'interface ListIterator : LinkedList<String> textes =...; ListIterator<String> it = textes.listiterator(); void add(e élément) Ajoute (insère) un élément avant la position courante. L'interface java.util.listiterator<e> void set(e élément) Remplace le dernier élément renvoyé par next() ou previous() par un nouvel élément. Déclenche une IllegalStateException si la structure de la liste a été modifiée depuis le dernier appel à next() ou à previous(). boolean hasprevious() Renvoie true s'il existe un élément avant la position courante.. E previous() Renvoie l'objet précédent. Déclenche une exception NoSuchElementException lorsque le début de la liste est atteinte. int nextindex() Renvoie l'indice de l'élément qui serait renvoyé par le prochain appel à next(). int previousindex() Renvoie l'indice de l'élément qui serait renvoyé par le prochain appel à previous(). Les méthodes de ListIterator 1. La méthode add() ajoute le nouvel élément avant la position de l'itérateur. Par exemple, le code suivant saute le premier élément de liste et ajoute la chaîne "Juliette" avant le deuxième élément : List<String> prénoms = new LinkedList<String>(); prénoms.add("claire"); prénoms.add("laurent"); prénoms.add("adrien"); ListIterator<String> itérateur = prénoms.listiterator(); itérateur.next(); // ignorer le premier élément itérateur.add("juliette"); Si vous appelez la méthode add() de ListIterator plusieurs fois, les éléments sont simplement ajoutés dans l'ordre dans lequel vous les passez. Ils sont tous ajoutés avant la position courante de l'itérateur. Comme pour toutes les classes concrètes, la classe LinkedList implémente une interface particulière (ou plusieurs), ici List, qui correspond à la fonctionnalité globale d'une liste. L'intérêt de déclarer une interface plutôt que la classe elle-même, je le rappelle, c'est qu'il est plus facile de changer ensuite le type de collection si le choix initial ne s'avère pas des plus judicieux à terme. Par exemple, si nous nous rendons compte que dans notre application, nous faisons très peu d'insertion ou de suppression d'éléments dans la liste, nous pourrions tout à fait prendre la classe ArrayList à la place de LinkedList puisque toutes les deux implémentent l'interface List. 2. Lorsque vous utilisez l'opération add() avec un itérateur qui vient juste d'être renvoyé par la méthode listiterator() et qui fait référence au début de la liste chaînée, les éléments sont ajoutés au début de liste chaînée. Lorsque l'itérateur dépasse le dernier élément de la liste (c'est-à-dire que la méthode hasnext() renvoie false), l'élément est ajouté à la fin de la liste. 3. Si la liste chaînée possède n éléments, il existe n+1 emplacements pour ajouter un nouvel élément. Ces emplacements correspondent aux n+1 positions possibles de l'itérateur. Par exemple, si une liste chaînée contient trois éléments, A, B et C, alors il existe quatre positions possibles pour insérer un nouvel élément (représentées par ) : ABC A BC AB C ABC Faites attention à l'analogie avec un curseur de texte. L'opération remove() de ListIterator ne fonctionne pas exactement comme la touche Retour-arrière. Juste après un appel à la méthode next(), la méthode remove() supprime en fait l'élément à gauche de l'itérateur, exactement comme la touche Retourarrière. Cependant, si vous venez d'appler la méthode previous(), l'élément situé à droite de l'itérateur sera supprimé. De plus, il n'est pas possible d'appeler la méthode remove() deux fois de suite. Contrairement à la méthode add(), qui ne dépend que de la position de l'itérateur, la méthode remove() dépend de l'état de l'itérateur. 4. De plus, il existe la méthode set() qui remplace le dernier élément renvoyé par next() ou previous() par un nouvel élément. Par exemple, le code suivant remplace le premier élément d'une liste par une nouvelle valeur : List<String> prénoms = new LinkedList<String>(); ListIterator<String> itérateur = prénoms.listiterator();

11 String anciennevaleur = itérateur.next(); itérateur.set("justine"); // affecte une nouvelle valeur au premier élément 5. Comme vous pouvez l'imaginer, si un itérateur parcourt une liste alors qu'un autre itérateur est en train de la modifier, il peut en résulter une certaine confusion. Par exemple, supposons qu'un itérateur fasse référence à un élément qu'un autre itérateur vient juste de supprimer. L'itérateur est désormais invalide et ne devrait donc plus être utilisé. Les itérateurs de listes chaînées ont été conçus pour détecter de telles modifications. Si un itérateur se rend compte que sa liste a été modifié par un autre itérateur ou par une méthode de la collection, il déclenche une exception ConcurrentModificationException : List<String> prénoms = new LinkedList<String>(); ListIterator<String> itérateur1 = prénoms.listiterator(); ListIterator<String> itérateur2 = prénoms.listiterator(); itérateur1.next(); itérateur1.remove(); itérateur2.next(); // déclenche une exception de type ConcurrentModificationException Afin d'éviter ce type d'exception, il suffit de suivre une règle simple : vous pouvez attacher autant d'itérateurs à une collection que vous le souhaitez, tant qu'ils effectuent uniquement des lectures dans la liste. Sinon, vous pouvez attacher un seul itérateur qui peut à la fois lire et écrire. Le déclenchement de ce type d'exception observe aussi une règle simple. La collection garde une trace du nombre d'opérations de transformations (comme l'ajout ou la suppression d'un élément). Chaque itérateur compte le nombre de transformations dont il est responsable. Au début de chaque méthode d'itérateur, ce dernier vérifie que son nombre de transformations est bien égal à celui de la collection. Dans le cas contraire, il déclenche l'exception ConcurrentModificationException. Il existe une curieuse exception à la règle permettant de détecter une modification simultanée. La liste chaînée garde uniquement la trace des modifications structurelles de la liste, comme l'ajout et la suppression de liens. La méthode set() n'est pas comptée comme une modification structurelle. Vous pouvez attacher plusieurs itérateurs à une liste chaînée, et ils peuvent tous appeler set() pour modifier le contenu des liens existants. Cette capacité est en fait nécessaire pour un certain nombre d'algorithmes de la classe Collections que nous aborderons plus loin dans cette étude. Retour aux méthode de la classe LinkedList Vous connaissez désormais les méthodes fondamentales de la classe LinkedList. Vous pouvez vous servir d'un ListIterator pour parcourir les éléments d'une liste chaînée dans n'importe quelle direction et pour ajouter ou supprimer des éléments. Comme nous l'avons vu dans la section précédente, plusieurs autres méthodes pratiques manipulent les listes chaînées, qui sont déclarées dans l'interface Collection. Ces méthodes, pour la plupart, sont implémentées dans la superclasse AbstractCollection de la classe LinkedList. 1. La méthode tostring() invoque les méthodes tostring() de chaque élément et renvoie une seule longue chaîne au format [A, B, C]. Cela est très pratique pour déboguer. 2. Utilisez la méthode contains() pour vérifier si un élément est présent dans une liste chaînée. La bibliothèque fournit aussi un certain nombre de méthodes qui sont quelque peu douteuses. Les listes chaînées ne permettent pas d'accéder rapidement à n'importe quel élément. Si vous voulez connaître le n-ième élément d'une liste chaînée, il faut commencer par le début de la liste et sauter les n-1 premiers éléments. Il n'existe aucun moyen plus rapide. Pour cette raison, les programmeurs n'utilisent généralement pas les listes chaînées dans des situations où les éléments doivent être référencés par un index entier. 1. Néanmoins, la classe LinkedList fournit une méthode get() qui vous permet d'accéder à un élément particulier : List<String> prénoms = new LinkedList<String>(); String valeur = prénoms.get(n); Bien sûr, cette technique n'est pas très efficace. Si vous vous rendez compte que vous l'utilisez, c'est probablement que vous utilisez une structure de données qui n'est pas adaptée à votre problème. Il ne faudrait jamais avoir recours à cette méthode pour parcourir les éléments d'une liste chaînée. L'efficacité du code suivant doit donc être vivement remise en question : List<String> prénoms = new LinkedList<String>(); for (int i=0; i < prénoms.size(); i++) utilisation de prénoms.get(i); Chaque fois que vous recherchez un élément, vous parcourez à nouveau la liste depuis le début. L'objet LinkedList ne fait aucun effort pour se rappeler la position courante du dernier élément lu. La méthode get() possède une légère optimisation ; si l'indice vaut au moins size()/2, la recherche commence par la fin de la liste. 2. L'interface de l'itérateur de liste possède en outre une méthode renvoyant l'indice de la position courante. En fait, comme les itérateurs Java pointent entre les éléments, il en existe deux : la méthode nextindex() renvoie l'indice de l'élément qui serait renvoyé par la méthode next(), et la méthode previousindex() retourne l'indice de l'élément qui serait renvoyé par la méthode previous(). Cette méthode correspond naturellement à celui de la méthode nextindex() moins un. Ces méthodes sont assez efficaces, car l'itérateur garde en mémoire sa position courante. 3. Enfin, si vous possédez un indice n, la méthode listiterator(n) de la classe LinkedList renvoie un itérateur qui pointe juste avant l'élément dont l'indice vaut n. C'est-à-dire qu'un appel à next() donnera le même élément que get(n). L'obtention de cet itérateur reste assez peu efficace. Si l'une de vos listes chaînées ne possède que quelques éléments, vous n'avez pas trop de soucis à vous faire à propos de l'efficacité des méthode get() et set(). Mais dans ce cas, quel est l'avantage d'une liste chaînée? La seule motivation permettant de choisir une liste chaînée consiste à minimiser le coût d'une insertion ou d'une suppression d'un élément en plein milieu de la liste. Si vous n'avez que quelques éléments à traiter, il vaut mieux utiliser une ArrayList.

12 Nous conseillons d'ailleurs d'éviter toutes les méthodes qui utilisent un indice d'entier permettant de noter la position dans une liste chaînée. Pour obtenir un accès direct dans une collection, mieux vaut utiliser un tableau ou encore un ArrayList. ListIterator<E> listiterator() Renvoie un itérateur bidirectionnel de liste permettant de parcourir les éléments de la liste. ListIterator<E> listiterator(int index) L'interface java.util.list<e> qui hérite de l'interface java.util.collection<e> Renvoie un itérateur bidirectionnel de liste permettant de parcourir les éléments de la liste dont le premier appel à next() doit renvoyer l'élément correspondant. void add(int index, E élément) Ajoute un élément à la position spécifiée. void addall(int index, Collection<? extends E> éléments) Ajoute tous les éléments d'une collection à la position spécifiée. E remove(int index) Supprime et renvoie l'élément à la position spécifiée. E get(int index) Récupère l'élément à la position spécifiée. E set(int index, E élément) Remplace l'élément à la position spécifiée par un nouvel élément et renvoie l'ancien élément. int indexoff(object élément) Renvoie la position de la première occurence d'un élément égal à l'élément spécifié, ou -1 si cet élément n'a pu être trouvé. int lastindexoff(object élément) Renvoie la position de la dernière occurence d'un élément égal à l'élément spécifié, ou -1 si cet élément n'a pu être trouvé. La classe java.util.linkedlist<e> qui implémente l'interface java.util.list<e> LinkedList() Construit une liste chaînée vide. LinkedList(Collection<? extends E> éléments) Construit une liste chaînée et y ajoute tous les éléments de la collection. void addfirst(e élément) void addlast(e élément) Ajoute un élément au début ou à la fin d'une liste. E getfirst() E getlast() Renvoie l'élément situé au début ou à la fin d'une liste. E removefirst() E removelast() Supprime et renvoient l'élément situé au début ou à la fin d'une liste. Exemple de mise en oeuvre A titre d'exemple, je vous propose de créer une application qui permet de gérer (surtout de visualiser) une liste de prénoms. Je profite de cette application pour voir comment effectuer les différents cas d'utilisations : ajout, suppression, changement de prénom, se déplacer dans la liste et tout effacer. package listes; import java.awt.*; import java.awt.event.actionevent; import javax.swing.*; import java.util.*; public class Prénoms extends JFrame { private JTextField nouveauprénom = new JTextField("Nouveau prénom", 18); private Résultat liste = new Résultat(); private JPanel panneau = new JPanel(); private JToolBar barre = new JToolBar(); private JTextField prénomactuel = new JTextField("(Vide)",18); private JTextField nombre = new JTextField("0"); private LinkedList<String> prénoms = new LinkedList<String>(); private ListIterator<String> itérateur = prénoms.listiterator(); public Prénoms() { super("liste de prénoms"); prénomactuel.seteditable(false); nombre.seteditable(false); nombre.sethorizontalalignment(jlabel.center); barre.add(new AbstractAction("Ajout") { itérateur.add(nouveauprénom.gettext()); codage correspondant

13 ); barre.add(new AbstractAction("Change") { if (itérateur.hasnext()) itérateur.set(nouveauprénom.gettext()); ); barre.add(new AbstractAction("Suppression") { if (itérateur.hasnext()) itérateur.remove(); ); barre.add(new AbstractAction("Tout effacer") { prénoms.clear(); itérateur = prénoms.listiterator(); ); barre.add(new AbstractAction("<<") { itérateur = prénoms.listiterator(); ); barre.add(new AbstractAction("<") { if (itérateur.hasprevious()) itérateur.previous(); ); barre.add(new AbstractAction(">") { if (itérateur.hasnext()) itérateur.next(); ); barre.add(new AbstractAction(">>") { itérateur = prénoms.listiterator(prénoms.size()); ); barre.addseparator(); barre.add(new JLabel("Nombre : ")); barre.add(nombre); add(barre, BorderLayout.NORTH); panneau.add(nouveauprénom); panneau.add(new JLabel("Actuel :")); panneau.add(prénomactuel); add(panneau); add(liste, BorderLayout.SOUTH); setsize(470, 135); setlocationrelativeto(null); setresizable(false); setdefaultcloseoperation(exit_on_close); setvisible(true); public void analyseprénoms() { liste.repaint(); if (prénoms.isempty()) prénomactuel.settext("(vide)"); else if (itérateur.hasnext()) { prénomactuel.settext(itérateur.next()); itérateur.previous(); else prénomactuel.settext("(fin de liste)"); nombre.settext(""+prénoms.size()); public static void main(string[] args) { new Prénoms(); private class Résultat extends JLabel { public Résultat() { setforeground(color.red); public void paint(graphics g) { settext(prénoms.tostring()); super.paint(g); 1. Vous remarquez que dans le cas d'une liste, nous nous servons presque essentiellement de l'itérateur bidirectionnel. 2. Le bouton ajout correspond finalement à une insertion. Les vecteurs (tableaux dynamiques) - Listes de tableaux - java.util.arraylist

14 Dans la section précédente, nous avons abordé l'interface List et la classe LinkedList qui l'implémente. L'interface List décrit une collection classée dans laquelle la position des éléments a une importance. Il existe deux protocoles pour parcourir ces éléments : avec un itérateur ou avec les méthodes d'accès direct get() et set(). Ces deux méthodes ne sont pas vraiment adaptées à une structure de liste chaînée, mais get() et set() prennent toute leur importance avec les tableaux. La bibliothèque de collections fournit une classe ArrayList qui implémente aussi l'interface List. Une ArrayList encapsule un tableau dynamique relogeable d'objets. 1. La classe ArrayList offre des fonctionnalités d'accès rapide comparables à celles d'un tableau d'objets. Bien qu'elle implémente, comme LinkedList, l'interface List, sa mise en oeuvre est différente et prévue pour permettre des accès efficaces à un élément de rang donné, c'est-à-dire un accès direct vers l'objet désiré, au travers d'un indice, comme dans le cas d'un tableau d'objets. 2. En outre, cette classe offre plus de souplesse que les tableaux d'objets dans la mesure où sa taille (son nombre d'éléments) peut varier au fil de l'exécution (comme celle de n'importe quelle collection). 3. Mais pour que l'accès direct à un élément de rang donné soit possible, il est nécessaire que les emplacements des objets (plutôt leurs références) soient contigus en mémoire (à la manière de ceux d'un tableau). Aussi cette classe souffrira d'une lacune inhérente à sa nature : l'insertion ou la suppression d'un objet à une position donnée ne pourra plus se faire aussi rapidement que dans le cas d'une liste chaînée puisque toutes les cases suivantes du tableau devront être décallées. En définitive, les vecteurs (tableaux dynamiques) seront bien adaptés à l'accès direct à condition que les insertions et les suppressions restent limitées. Il n'existe pas de méthodes réellement spécifiques à la classe ArrayList. Tout ce que nous connaissons déjà peut donc être pris en compte. Finalement, il suffit juste de connaître l'efficacité des méthodes utilisées. Suivant le cas, il peut être judicieux de choisir entre la classe ArrayList et la classe LinkedList. Exemple de mise en oeuvre A titre d'exemple, je vous propose de reprendre l'application précédente et d'implémenter la classe ArrayList à la place de la classe LinkedList. Cette fois-ci, je passerai par un indice plutôt que de passer par un itérateur. package listes; codage correspondant import java.awt.*; import java.awt.event.actionevent; import java.awt.event.actionlistener; import javax.swing.*; import java.util.*; import javax.swing.event.*; public class Prénoms extends JFrame { private JTextField nouveauprénom = new JTextField("Nouveau prénom", 19); private Résultat liste = new Résultat(); private JPanel panneau = new JPanel(); private JToolBar barre = new JToolBar(); private JTextField prénomactuel = new JTextField("(Vide)",19); private JTextField nombre = new JTextField("0"); private JSpinner position = new JSpinner(); private ArrayList<String> prénoms = new ArrayList<String>(); private int indice; public Prénoms() { super("liste de prénoms"); prénomactuel.seteditable(false); position.setpreferredsize(new Dimension(45, 22)); position.addchangelistener(new ChangeListener() { public void statechanged(changeevent e) { int index = (Integer)position.getValue(); if (index>=0 && index<prénoms.size()) indice = index; else position.setvalue(indice); ); nombre.seteditable(false); nombre.sethorizontalalignment(jlabel.center); AbstractAction ajout = new AbstractAction("Ajout") { prénoms.add(nouveauprénom.gettext()); ; barre.add(ajout); nouveauprénom.addactionlistener(ajout); barre.add(new AbstractAction("Change") { prénoms.set(indice, nouveauprénom.gettext()); ); barre.add(new AbstractAction("Suppression") { prénoms.remove(indice--); position.setvalue(indice); ); barre.add(new AbstractAction("Tout effacer") {

15 prénoms.clear(); position.setvalue(indice = 0); ); barre.addseparator(); barre.add(new JLabel("Position : ")); barre.add(position); barre.addseparator(); barre.add(new JLabel("Nombre : ")); barre.add(nombre); add(barre, BorderLayout.NORTH); panneau.add(nouveauprénom); panneau.add(new JLabel("Actuel :")); panneau.add(prénomactuel); add(panneau); add(liste, BorderLayout.SOUTH); setsize(490, 135); setlocationrelativeto(null); setresizable(false); setdefaultcloseoperation(exit_on_close); setvisible(true); public void analyseprénoms() { liste.repaint(); if (prénoms.isempty()) prénomactuel.settext("(vide)"); else prénomactuel.settext(prénoms.get(indice)); nombre.settext(""+prénoms.size()); public static void main(string[] args) { new Prénoms(); private class Résultat extends JLabel { public Résultat() { setforeground(color.red); public void paint(graphics g) { settext(prénoms.tostring()); super.paint(g); Lorsque nous consultons ce code en regard du même exemple traité par une liste chaînée, nous remarquons tout de suite que nous travaillons plus directement avec le tableau dynamique (ArrayList) sans passer systématiquement par un itérateur, ce qui etait très souvent le cas avec la classe LinkedList. Je préfère très nettement cette approche puiqu'elle me paraît plus intuitive. Autre exemple de mise en oeuvre Ci-dessous se trouve un exemple où nous prenons comme collection un tableau dynamique ArrayList. Les méthodes utilisées sont les méthodes communes aux deux types de collection. Remplacez, d'ailleurs, la classe ArrayList par LinkedList, vous remarquerez que votre programme vous donnera le même résultat. Le choix entre les deux types de collection doit correspondre uniquement au critère de performance. Si vous devez insérer ou supprimer des éléments n'importe où dans la collection, il faut alors choisir LinkerList. Si vous n'avez pas ce genre de contrainte et que vous voulez juste remplacer un tableau statique par un tableau dynamique, n'hésitez pas à prendre le conteneur ArrayList. import java.util.*; import static java.lang.system.*; public class TableauDynamique { public static void main(string[] args) { // tableau d'entier par la classe enveloppe Integer ArrayList<Integer> tableau = new ArrayList<Integer>(); out.println("tableau vide? "+tableau.isempty()); tableau.add(-45); tableau.add(13); tableau.add(24); affichetableau(tableau); // ajout de la valeur 89 à la position 1 tableau.add(1, 89); affichetableau(tableau); // remplacer la valeur 89 par la valeur 3 tableau.set(1, 3); affichetableau(tableau); // enlève la valeur 13 if (tableau.contains(13)) tableau.remove((integer)13); affichetableau(tableau); //ajout d'une nouvelle valeur et affichage en ligne tableau.add(-45); out.print("tableau[] = { "); for(integer valeur : tableau) out.print(valeur+" "); out.println(""); // position de la valeur -45 out.println("position de la valeur -45 : "+tableau.indexof(-45)); out.println("position de la valeur -45 : "+tableau.lastindexof(-45)); // effacer le tableau en entier tableau.clear(); out.println("taille du tableau : "+tableau.size()); public static void affichetableau(arraylist<integer> tableau) { out.println("taille du tableau : "+tableau.size()); for (int i=0; i<tableau.size(); i++) out.println("tableau["+i+"] = "+tableau.get(i)); Codage avec résultats en correspondance Résultat du programme init: deps-jar: Compiling 1 source file to L:\BTS IRIS\TP Java\ListArray\build\classes compile: run: Tableau vide? true Taille du tableau : 3 tableau[0] = -45 tableau[1] = 13 tableau[2] = 24 Taille du tableau : 4 tableau[0] = -45 tableau[1] = 89 tableau[2] = 13 tableau[3] = 24 Taille du tableau : 4 tableau[0] = -45 tableau[1] = 3 tableau[2] = 13 tableau[3] = 24 Taille du tableau : 3 tableau[0] = -45 tableau[1] = 3 tableau[2] = 24 tableau[] = { Position de la valeur -45 : 0 Position de la valeur -45 : 3 Taille du tableau : 0 BUILD SUCCESSFUL (total time: 0 seconds)

16 Ensembles d'objets uniques - HashSet, TreeSet, EnumSet et LinkedHashSet Les listes chaînées et les tableaux vous permettent de spécifier l'ordre dans lequel vous voulez organiser vos éléments. Cependant, si vous recherchez un élément particulier et que vous ne vous rappeliez pas sa position, vous aurez besoin de parcourir tous les éléments jusqu'à ce que vous trouviez l'élément recherché. Cela requiert beaucoup de temps, surtout lorsque la collection contient pas mal d'éléments. Si l'ordre des éléments n'a pas d'importance, il existe des structures de données qui vous permettent de retrouver un élement très rapidement. L'inconvénient est que ces structures de données ne vous permettent pas de contrôler l'ordre des éléments. En effet, elles les organisent plutôt selon l'ordre qui leur permet de les retrouver facilement. Ces structures de données correspondent tout-à-fait à la théorie des ensembles. Trois classes implémentent la notion d'ensemble : HashSet, TreeSet et EnumSet. Rappelons que, théoriquement, un ensemble est une collection non ordonnée d'éléments, aucun élément ne pouvant apparaître plusieurs fois dans un même ensemble. Chaque fois que nous introduisons un nouvel élément dans une collection de ce type, il est donc nécessaire de s'assurer qu'il n'y figure pas déjà, autrement dit que l'ensemble ne contient pas un autre élément qui lui soit égal. Attention : Dès que nous nous écartons des types String, File ou classe enveloppe de type primitif, il est généralement nécessaire de se préoccuper des méthodes equals() ou compareto() (ou d'un comparateur - voir plus loin) ; si nous ne le faisons pas, il faut alors accepter que deux objets de références différentes ne soient jamais identiques, quelles que soit leurs valeurs! Par ailleurs, bien qu'en théorie un ensemble ne soit pas ordonné, des raisons évidentes d'efficacité de méthodes de test d'appartenance nécessite une certaine organisation de l'information. Dans le cas contraire, un tel test d'appartenance ne pourrait se faire qu'en examinant un à un les éléments de l'ensemble (ce qui conduirait à une efficacité moyenne). Quatre démarches différentes ont été employées par les concepteurs des collections, d'où l'existence de quatre classes différentes : 1. HashSet : qui recourt à une technique dite de hachage, qui mémorise ses éléments dans un ordre quelconque (et donc plus rapide si l'ordre n'a pas d'importance). 2. TreeSet : qui utilise un arbre binaire pour ordonner complètement les éléments. TreeSet mémorise ses éléments dans l'ordre ascendant avec un arbre, pour maintenir plus rapidement le tri des éléments stockées. 3. EnumSet : est une implémentation efficace d'un ensemble, dont les éléments appartiennent à un type énuméré. Un type énuméré ayant un nombre fini d'instances, EnumSet est implémenté en interne sous la forme d'une suite de bits. Un bit est activé si la valeur correspondante est présente dans l'ensemble. 4. LinkedHashSet : Un ensemble tout à fait particulier qui se souvient de l'ordre d'insertion des éléments. En définitive, dans tous les cas, les éléments sont ordonnées même si cet ordre est moins facile à appréhender ; avec TreeSet, il s'agit de l'ordre induit par compareto() ou un éventuel comparateur personnalisé. Phase de création d'un ensemble pour les quatre types de collection Comme toute collection, un ensemble peut être construit vide ou à partir d'une autre collection : La classe java.util.hashset<e> HashSet() Construit un ensemble vide. HashSet(Collection<? extends E> éléments) Construit un nouvel ensemble et y ajoute tous les éléments de la collection. HashSet(int capacité) Construit un ensemble vide avec la capacité spécifiée. La classe java.util.treeset<e> TreeSet() Construit un ensemble trié vide pour stocker les objets de type Comparable. TreeSet(Comparator<? super E> éléments) Construit un ensemble trié vide et se sert du comparateur spécifié pour trier ses éléments. TreeSet(SortedSet<? extends E> éléments) Construit un ensemble trié vide, ajoute tous les éléments d'un ensemble trié et se sert du même comparateur que celui de l'ensemble spécifié. La classe java.util.enumset<e extends Enum<E>> static <E extends Enum<E>> EnumSet<E> allof(class<e> typeenumération) Renvoie un ensemble contenant toutes les valeurs du type énuméré donné. static <E extends Enum<E>> EnumSet<E> noneof(class<e> typeenumération) Renvoie un ensemble vide capable de contenir des valeurs du type énuméré donné. static <E extends Enum<E>> EnumSet<E> range(e de, E à) Renvoie un ensemble contenant toutes les valeurs comprises entre de et à (compris). static <E extends Enum<E>> EnumSet<E> of(e valeur) static <E extends Enum<E>> EnumSet<E> of(e valeur, E... valeurs) Renvoie un ensemble contenant les valeurs données. La classe EnumSet ne possède pas de constructeurs publics. Utilisez une méthode de fabrique statique (qui génère une instance appropriée) pour construire l'ensemble désiré. LinkedHashSet() Construit un ensemble vide. LinkedHashSet(Collection<? extends E> éléments) Construit un nouvel ensemble et y ajoute tous les éléments de la collection. LinkedHashSet(int capacité) Construit un ensemble vide avec la capacité spécifiée. La classe java.util.linkedhashset<e>

17 Méthodes communes à ces différents ensembles Ces ensembles disposent tous de méthodes communes puisque, comme pour les autres collections, elles implémentent l'interface Collection. Je rappelle, ci-dessous les méthodes bien utiles 1. Parcourir la collection : iterator() : cette méthode fournit un itérateur monodirectionnel (Iterator) permettant de parcourir les différents éléments de la collection : HashSet<Interger> loto =...; // TreeSet<Integer> loto =...; Iterator<Integer> it = loto.iterator(); while (it.hasnext()) { int élément = it.next(); // utilisation de élément 2. Ajout d'un élément - add() : Rappelons qu'il est impossible d'ajouter un élément à une position donnée puisque les ensembles ne disposent pas d'itérateur bidirectionnel. D'ailleurs, comme au niveau de leur implémentation, les ensembles sont organisés en fonction des valeurs de leurs éléments, l'opération ne serait pas réalisable. La seule façon d'ajouter un élément à un ensemble est d'utiliser la méthode add() prévue par l'interface Collection. Elle assure en effet que l'élément en question n'existe pas déjà : HashSet<Interger> loto =...; // TreeSet<Integer> loto =...;... boolean existe = loto.add(23); if (existe) System.out.println(23 + " existe déjà"); else System.out.println(23 + " a été ajouté"); Nous ne pouvons pas dire que add() ajoute l'élément en "fin d'ensemble" (comme dans le cas des collections étudiées précédemment). En effet, comme nous l'avons déjà évoqué, les ensembles sont organisés au niveau de leur implémentation, de sorte que l'élément devra quand même être ajouté à un endroit bien précis de l'ensemble (endroit qui se concrétisera lorsque nous utiliserons un itérateur). Pour l'instant, nous le devinons déjà, cet endroit sera imposé par l'ordre induit par compareto() (ou par un comparateur personnalisé) dans le cas de TreeSet ; en ce qui concerne HashSet, nous verrons bientôt comment l'emplacement est déterminé. 3. Suppression d'un élément - remove() : Nous avons vu que pour les autres collections, la méthode remove() de suppression d'une valeur donnée est d'une efficacité très moyenne. Un des grands avantages des ensembles est d'effectuer cette opération avec une très grand efficacité. Ici, la méthode remove() renvoie true si l'élément a été trouvé (et donc supprimé) et false dans le cas contraire : TreeSet<Personnel> employés =...;... boolean trouvé = employés.remove(employé); if (trouvé) System.out.println(employé + " a été supprimé"); else System.out.println(employé + " n'existe pas"); Par ailleurs, la méthode remove() de l'itérateur monodirectionnel permet de supprimer l'élément courant (le dernier renvoyé par next()) le cas échéant : HashSet<Interger> loto =...; Iterator<Integer> it = loto.iterator(); it.next(); it.next(); it.remove(); // supprime le deuxième élément 4. Teste l'existence d'un élément - contains() : cette méthode permet de tester l'existence d'un élément avec, pour les ensembles, une très grande efficacité. 5. Opérations ensemblistes : Les méthodes removeall(), addall() et retainall(), applicables à toutes les collections, prennent un intérêt tout particulier avec les ensembles. Elles bénificient de l'efficacité de l'accès à une valeur donnée. Ainsi si e1 et e2 sont deux ensembles : Union : e1.addall(e2) : place dans e1 tous les éléments présents dans e2. Après exécution, la réunion de e1 et e2 se trouve dans e1 (dont le contenu a généralement été modifié). Intersection : e1.retainall(e2) : garde dans e1 ce qui appartient à e2. Après exécution, nous obtenons l'intersection de e1 et de e2 dans e1 (dont le contenu a généralement été modifié). Complément de e2 : e1.removeall(e2) : supprime de e1 tout ce qui appartient à e2. Après exécution, nous obtenons le complémentaire de e2 par rapport à e1 dans e1 (dont le contenu a généralement été modifié). Les ensembles de type HashSet La classe HashSet représente un ensemble d'éléments non ordonnés. De ce fait, elle offre une solution de recherche extrêmement efficace. Cette efficacité est dû à sa structure interne et à sa méthode d'enregistrement des données. La structure de données classique pour retrouver simplement un élément est "la table de hachage". 1. Table de hachage : Une table de hachage est une organisation des éléments d'une collection qui permet de retrouver facilement un élément de valeur donnée. Pour cela, la table de hachage calcule, à l'aide de la méthode spécifique hashcode() dite "fonction de hachage", un entier, appelé "code de hachage", pour chacun des éléments. Un même entier peut correspondre à plusieurs valeurs différentes. En revanche, deux éléments de même valeur doivent toujours fournir le même code de hachage. 2. Code de hachage : Un code de hachage est un entier dérivé des champs d'instance d'un objet, pour que les objets contenant différentes données produisent des codes différents. Le tableau ci-dessous présente quelques exemples de codes de hachage nés de la méthode hashcode() de la classe String : Chaîne "Emmanuel" "emmanuel" "manu" int hashcode() { int hash = 0; for (int i=0; i<length(); i++) hash = 31 * hash + charat(i); return hash; Code de hachage méthode hashcode() de la classe String Jusqu'ici, nous n'avons considéré que des ensembles dont les éléments étaient d'un type String ou de classes enveloppes (comme Integer, Double, etc.) pour lesquels, nous n'avions pas à nous préoccuper des détails d'implémentation. Dès que nous cherchons à utiliser des éléments d'un autre type

18 d'objet, il est nécessaire de connaître quelques conséquences de la manière dont les ensembles sont effectivement implémentés. Plus précisément, dans le cas de HashSet, vous devrez définir convenablement : - la méthode equals() : c'est toujours elle qui sert à définir l'appartenance d'un élément à l'ensemble. si a.equals(b), a et b doivent avoir le même code de hachage. - la méthode hashcode() dont nous allons voir comment elle est exploitée pour ordonnancer les éléments d'un ensemble. Les codes de hachage peuvent être calculés très rapidement et ce calcul ne dépend que de l'état de l'objet à hacher, et non des autres objets de la table de hachage. 3. Code hachage par défaut : La méthode hashcode() est définie dans la classe Object. Chaque objet possède donc un code de hachage par défaut, extrait de l'adresse mémoire de l'objet. Etudions l'exemple suivant : String s = "Ok"; StringBuilder sb = new StringBuilder(s); System.out.println(s.hashCode() + " " + sb.hashcode()); String t = new String("Ok"); StringBuilder tb = new StringBuilder(t); System.out.println(t.hashCode() + " " + tb.hashcode()); Chaîne s 2556 sb t 2556 tb Code de hachage Sachez que les chaînes s et t présentent le même code de hachage car, pour les chaînes, ces codes sont dérivés de leur contenu. Par contre, les constructeurs de chaînes sb et tb disposent de code de hachage différents car aucune méthode hashcode() n'a pas été redéfinie pour la classe StringBuilder et la méthode hashcode() de la classe Object dérive le code de hachage de l'adresse mémoire de l'objet. Si vous redéfinissez la méthode equals(), vous devrez aussi redéfinir la méthode hashcode() pour les objets que l'utilisateur pourraient insérer dans une table de hachage afin qu'il soit facile par la suite de les retrouver rapidement et surtout que deux objets de valeurs identiques puissent produire le même code de hachage, ce que ne fait pas la méthode hashcode() de Object. La méthode hashcode() doit renvoyer un entier. Associez simplement les codes de hachage des attributs de l'objet, de sorte que les codes des différents objets soient plus largement répartis. 4. Structure de la table de hachage : En Java, les tables de hachage, sont relativement sophistiquées, et sont implémentées sous forme de tableaux de listes chaînées. Chaque case du tableau dispose ainsi d'une liste qui est souvent appelée un seau (ou panier, en encore bucket). Initialement les seaux sont vides. A chaque ajout d'un élément dans la collection, elle lui attribut un emplacement dans un des seaux dont le rang n (dans le tableau de seaux) est défini en fonction de son code de hachage et réduit par le modulo du nombre total de seaux : seau = codehachage % NombreDeSeaux Par exemple, si un objet possède un code de hachage de et qu'il existe 128 seaux, l'objet sera placé dans le seau 108 (car le reste de la division entière 76268/128 est 108). Avec un peu de chance, il n'existe aucun autre élément dans ce seau. Il suffit alors d'insérer l'élément dans le seau. Naturellement, il est inévitable de trouver parfois un seau déjà utilisé. Cela s'appelle une collision de hachage. Dans ce cas, vous comparez le nouvel objet avec tous les autres objet du seau pour déterminer s'il en fait déjà partie. S'il existe effectivement des éléments dans le seau, le nouvel élément est ajouté à la fin de la liste chaînée correspondante. En se fondant sur le fait que les codes de hachage sont distribués aléatoirements et que le nombre de seaux est suffisament grand, il suffit généralement d'effectuer que quelques petites comparaisons. Si vous désirez contrôler plus finement les performances d'une table de hachage, il est possible de spécifier le nombre initial de seaux. Si trop d'éléments sont insérés dans la table de hachage, le nombre de collisions augmente et les performances de recherche baissent. Comme nous pouvons nous y attendre, le choix du nombre initial de seaux sera fait en fonction du nombre d'éléments prévus pour la collection. On nomme "facteur de charge" le rapport entre le nombre d'éléments de la collection et le nombre de seaux. Plus ce facteur est grand, moins statistiquement, nous obtenons des seaux contenant plusieurs éléments ; par contre, plus il est grand, plus le tableau de références des seaux occupe de l'espace. Généralement, nous choisissons un facteur de charge de l'ordre de 75%. Bien entendu, la méthode de hachage joue également un rôle important dans la bonne répartition des codes des éléments dans les différents seaux. Pour retrouver un élément de la collection (ou pour savoir s'il est présent), on détermine d'abord son code de hachage. Ensuite la formule seau = codehachage % NombreDeSeaux donne la valeur du seau dans lequel l'élément est succeptible de se trouver. Il ne reste plus qu'à parcourir les différents éléments du seau pour vérifier si la valeur donnée s'y trouve (à l'aide de la méthode equals()). Notez que nous utilisons cette méthode equals() que pour les seuls éléments qui sont dans le même seau. Avec Java, les tables de hachage sont automatiquement agrandies dès que leur facteur de charge devient trop grand (supérieur à 75%). Elles sont automatiquement réorganisées avec le double de seaux.

19 5. Ajout et vérification d'un élément dans l'ensemble : Les tables de hachage peuvent servir à implémenter plusieurs structures de données importantes. La plus simple d'entre elles est la classe qui implémente directement l'interface Set. Un Set correspond simplement à une collection d'éléments ne figurant qu'une seule fois chacun dans la collection. La méthode add() d'un Set commence par essayer de retrouver l'objet à ajouter et ne l'ajoute que s'il n'est pas encore présent. La bibliothèque de collections Java fournit une classe HashSet qui implémente un Set à partir d'une table de hachage. Les éléments sont ajoutés avec la méthode add(). La méthode contains(), que nous connaissons déjà, est redéfinie pour effectuer une recherche rapide et vérifier si un des éléments fait déjà partie du Set. Elle ne vérifie les éléments que d'un seul seau et non tous les éléments de la collection. L'itérateur d'un Set parcourt tous les seaux un par un. Comme les éléments d'une table de hachage sont dispersés dans toute la table, les seaux sont parcourus dans un ordre pseudo aléatoire. C'est pourquoi, il ne faut utiliser un HashSet que si l'ordre des éléments dans la collection n'a aucune importance. Exemple d'application d'un ensemble de prénoms Après toutes ces considérations techniques, il est temps de prendre un exemple. Je vais reprendre l'application précédente et placer une collection de type HashSet à la place de ArrayList et prévoir ainsi les fonctionnalités justes nécessaires à la gestion de l'ensemble des prénoms. Cette fois-ci, dans cette nouvelle application, un prénom doit être exclusif. Remarquez bien que les prénoms se place à priori de façon tout à fait aléatoirement dans la collection. Vu que nous utilisons comme éléments de collection des chaînes de caractères, il n'est pas nécessaire de se préoccuper des méthodes equals() et hashcode(). package ensemble; import java.awt.*; import java.awt.event.actionevent; import javax.swing.*; import java.util.*; public class Prénoms extends JFrame { private JTextField nouveauprénom = new JTextField("Nouveau prénom", 18); private JCheckBox existe = new JCheckBox("Existe"); private Résultat liste = new Résultat(); private JPanel panneau = new JPanel(); private JToolBar barre = new JToolBar(); private JTextField nombre = new JTextField("0"); private HashSet<String> prénoms = new HashSet<String>(); public Prénoms() { super("ensemble de prénoms"); nombre.seteditable(false); nombre.sethorizontalalignment(jlabel.center); Action ajouter = new AbstractAction("Ajout") { prénoms.add(nouveauprénom.gettext()); ; barre.add(ajouter); barre.add(new AbstractAction("Suppression") { prénoms.remove(nouveauprénom.gettext()); ); barre.add(new AbstractAction("Tout effacer") { prénoms.clear(); ); barre.add(new AbstractAction("Existe?") { existe.setselected(prénoms.contains(nouveauprénom.gettext())); ); barre.addseparator(); barre.add(new JLabel("Nombre : ")); barre.add(nombre); add(barre, BorderLayout.NORTH); nouveauprénom.addactionlistener(ajouter); panneau.add(nouveauprénom); panneau.add(existe); add(panneau); add(liste, BorderLayout.SOUTH); setsize(470, 135); setlocationrelativeto(null); setresizable(false); setdefaultcloseoperation(exit_on_close); setvisible(true); public void analyseprénoms() { liste.repaint(); nombre.settext(""+prénoms.size());

20 existe.setselected(prénoms.contains(nouveauprénom.gettext())); public static void main(string[] args) { new Prénoms(); private class Résultat extends JLabel { public Résultat() { setforeground(color.red); public void paint(graphics g) { settext(prénoms.tostring()); super.paint(g); 6. La méthode hashcode() : Elle est donc utilisée pour calculer le code de hachage d'un objet. Les classe String, File et les classes enveloppes définissent une méthode hashcode() utilisant la valeur effective des objets. En revanche, les autres classes ne redéfinissent pas hashcode() et recourent à la méthode hashcode() héritée de la classe Object, laquelle se contente d'utiliser comme "valeur" la simple adresse des objets. Dans ces conditions, deux objets différents de même valeur auront toujours des codes de hachage différents. Si nous souhaitons pouvoir définir une égalité des éléments basée sur leur valeur effective, il va donc falloir redéfinir dans la classe correspondante la méthode hashcode(). Elle doit effectivement fournir le code de hachage correspondant à la valeur intrinsèque de l'objet et non plus de son adresse. Dans la définition de cette méthode hashcode(), il ne faudra pas oublier que le code de hachage doit être compatible avec equals(). Deux objets égaux pour equals() doivent absolument fournir le même code, sinon ils risquent d'aller dans des seaux différents ; dans ce cas, il n'apparaîtrons plus comme égaux (puisque le système recourt à la méthode equals() qu'à l'intérieur d'un même seau). De même, nous ne pouvons nous permettre de redéfinir seulement equals() sans redéfinir la méthode hashcode(). Exemple d'application d'un ensemble de coordonnées Cette fois-ci, nous allons mettre en oeuvre une nouvelle application qui stocke un ensemble de coordonnées. Ici aussi chaque point doit être exclusif. Je décris une nouvelle classe Coordonnées qui enregistre la valeur de x et de y de la position d'un point. Remarquez bien également que les coordonnées du point se placent à priori de façon tout à fait aléatoirement dans la collection. Cette fois-ci, vu que je crée une nouvelle classe, cette dernière doit redéfinir respectivement les méthodes equals(), hashcode() et tostring(). package ensemble; import java.awt.*; import java.awt.event.actionevent; import javax.swing.*; import java.util.*; public class EnsemblePoints extends JFrame { private JFormattedTextField abscisse = new JFormattedTextField(0); private JFormattedTextField ordonnée = new JFormattedTextField(0); private JCheckBox existe = new JCheckBox("Existe"); private Résultat liste = new Résultat(); private JPanel panneau = new JPanel(); private JToolBar barre = new JToolBar(); private JFormattedTextField nombre = new JFormattedTextField(0); private HashSet<Coordonnées> ensemble = new HashSet<Coordonnées>(); public EnsemblePoints() { super("ensemble de prénoms"); nombre.seteditable(false); nombre.sethorizontalalignment(jlabel.center); Action ajouter = new AbstractAction("Ajout") { ensemble.add(new Coordonnées((Integer)abscisse.getValue(), (Integer)ordonnée.getValue())); analyse(); ; barre.add(ajouter); barre.add(new AbstractAction("Suppression") { ensemble.remove(new Coordonnées((Integer)abscisse.getValue(), (Integer)ordonnée.getValue())); analyse(); ); barre.add(new AbstractAction("Tout effacer") { ensemble.clear(); analyse(); ); barre.add(new AbstractAction("Existe?") { existe.setselected(ensemble.contains(new Coordonnées((Integer)abscisse.getValue(), (Integer)ordonnée.getValue()))); ); barre.addseparator();

21 barre.add(new JLabel("Nombre : ")); barre.add(nombre); add(barre, BorderLayout.NORTH); panneau.add(new JLabel("x : ")); abscisse.setcolumns(4); panneau.add(abscisse); panneau.add(new JLabel("y : ")); ordonnée.addactionlistener(ajouter); ordonnée.setcolumns(4); panneau.add(ordonnée); panneau.add(existe); add(panneau); add(liste, BorderLayout.SOUTH); setsize(470, 135); setlocationrelativeto(null); setresizable(false); setdefaultcloseoperation(exit_on_close); setvisible(true); public void analyse() { liste.repaint(); nombre.setvalue(ensemble.size()); existe.setselected(ensemble.contains(new Coordonnées((Integer)abscisse.getValue(), (Integer)ordonnée.getValue()))); public static void main(string[] args) { new EnsemblePoints(); private class Résultat extends JLabel { public Résultat() { setforeground(color.red); public void paint(graphics g) { settext(ensemble.tostring()); super.paint(g); private class Coordonnées { private int x, y; public Coordonnées(int x, int y) { this.x = x; this.y = public boolean equals(object obj) { Coordonnées c = (Coordonnées) obj; return x==c.x && public int hashcode() { return public String tostring() { return "("+x+", "+y+")"; Les ensembles de type TreeSet Nous venons de voir comment les ensembles HashSet organisent leurs éléments en table de hachage, en vue de les retrouver rapidement. La classe TreeSet propose une autre organisation utilisant un arbre binaire, lequel permet d'ordonner totalement les éléments. Nous utilisons, cette fois-ci, la relation d'ordre ususelle induite par la méthode compareto() ou par un comparateur personnalisé. Dans ces conditions, la recherche dans cet arbre d'un élément particulier est généralement moins rapide que dans la table de hachage mais tout de même plus rapide qu'une recherche séquentielle, comme avecun ArrayList ou avec un LinkedList. Par ailleurs, l'utilisation d'un arbre binaire permet de disposer en permanence d'un ensemble totalement ordonné (trié). Nous noterons d'ailleurs que la classe TreeSet dispose de deux méthodes spécifiques : first() et last() fournissant respectivement le premier et le dernier élément de l'ensemble. Nous noterons bien que, dans un ensemble TreeSet, la méthode equals() n'intervient ni dans l'organisation de l'ensemble, ni dans le test d'appartenance d'un élément. L'égalité est définie uniquement à l'aide de la méthode compareto() (ou d'un comparateur personnalisé). Dans un ensemble HashSet, la méthode equals() intervenait (mais uniquement pour des éléments de même numéro de seau). La classe TreeSet ressemble beaucoup à la classe HashSet, avec une amélioration supplémentaire. Les arbres de hachage représentent des collections triées. Vous pouvez insérer des éléments dans la collection dans n'importe quel ordre, et lorsque vous parcourez ces éléments, ils sont automatiquement présentés selon un ordre croissant. Comme le nom de la classe le laisse entendre, le tri est accompli par une structure de données en arbre. Exemple d'application d'un ensemble de prénoms ordonnés Reprenons tout de suite l'exemple sur la gestion des prénoms. Cette fois-ci, notre ensemble de prénoms sera ordonné dans la collection. Pour aboutir à ce résultat, il suffit tout simplement de remplacer la classe HashSet par la classe TreeSet lors de la déclaration de la collection. Pour le reste, nous pouvons conserver la même gestion. Vu que nous utilisons comme éléments de collection des chaînes de caractères, la méthode compareto() de la classe String a été redéfinie pour connaître si une chaîne doit être placée avant ou après une autre dans la collection, ce qui justifie cette simplicité d'écriture.

22 package ensemble; import java.awt.*; import java.awt.event.actionevent; import javax.swing.*; import java.util.*; public class Prénoms extends JFrame { private JTextField nouveauprénom = new JTextField("Nouveau prénom", 18); private JCheckBox existe = new JCheckBox("Existe"); private Résultat liste = new Résultat(); private JPanel panneau = new JPanel(); private JToolBar barre = new JToolBar(); private JTextField nombre = new JTextField("0"); private TreeSet<String> prénoms = new TreeSet<String>(); public Prénoms() { super("ensemble de prénoms"); nombre.seteditable(false); nombre.sethorizontalalignment(jlabel.center); Action ajouter = new AbstractAction("Ajout") { prénoms.add(nouveauprénom.gettext()); ; barre.add(ajouter); barre.add(new AbstractAction("Suppression") { prénoms.remove(nouveauprénom.gettext()); ); barre.add(new AbstractAction("Tout effacer") { prénoms.clear(); ); barre.add(new AbstractAction("Existe?") { existe.setselected(prénoms.contains(nouveauprénom.gettext())); ); barre.addseparator(); barre.add(new JLabel("Nombre : ")); barre.add(nombre); add(barre, BorderLayout.NORTH); nouveauprénom.addactionlistener(ajouter); panneau.add(nouveauprénom); panneau.add(existe); add(panneau); add(liste, BorderLayout.SOUTH); setsize(470, 135); setlocationrelativeto(null); setresizable(false); setdefaultcloseoperation(exit_on_close); setvisible(true); public void analyseprénoms() { liste.repaint(); nombre.settext(""+prénoms.size()); existe.setselected(prénoms.contains(nouveauprénom.gettext())); public static void main(string[] args) { new Prénoms(); private class Résultat extends JLabel { public Résultat() { setforeground(color.red); public void paint(graphics g) { settext(prénoms.tostring()); super.paint(g); Depuis Java SE 6, les ensembles TreeSet implémentent en outre l'interface NavigableSet qui prévoit des méthodes exploitant l'ordre total induit par l'organisation de l'ensemble (en un arbre binaire). Ces méthodes peuvent retrouver et, éventuellement, de supprimer l'élément le plus petit ou le plus grand au sens de cet ordre, ou encore de trouver l'élément le plus proche (avant ou après) d'une valeur donnée. Il est possible de parcourir les éléments dans l'ordre inverse de l'ordre naturel.

23 Enfin, certaines méthodes permettent d'obtenir une vue (cette notion sera présentée ultérieurement) d'une partie de l'ensemble, formée des éléments de valeur supérieure ou inférieure à une valeur donnée. E higher(e valeur) E lower(e valeur) Renvoie la > valeur du plus petit élément ou la < valeur du plus grand élément ou encore null si cet élément n'existe pas. E ceiling(e valeur) E floor(e valeur) Renvoie la >= valeur du plus petit élément ou la <= valeur du plus grand élément ou encore null si cet élément n'existe pas. E pollfirst() E polllast() Suppriment et renvoient le plus petit ou le plus grand élément de cet ensemble ou encore null si l'ensemble est vide. Iterator<E> descendingiterator() Renvoie un itérateur qui parcourt cet ensemble par ordre décroissant. L'interface java.util.navigableset<e> Comparaison d'objets Comment TreeSet (ou TreeMap dans le chapitre suivant) sait-il dans quel ordre trier les éléments? Par défaut, cette structure de données part de l'hypothèse que vous insérez des éléments qui implémentent l'interface Comparable, ce qui est d'ailleurs le cas des classes que nous avons déjà utilisé comme la classe Integer ou la classe String. Que se passe t-il si nous essayons de stocker des objets qui n'implémentent pas cette interface? Si vous tentez d'exécuter le code ci-dessous, vous obtiendrez une erreur d'exécution (Exception levée). Effectivement, nous créons une nouvelle classe Personne sans s'occuper de l'interface Comparable, ainsi le système est incapable de placer ces personnes dans l'ordre croissant puisque nous ne lui indiquons pas comment faire. import java.util.*; import static java.lang.system.*; public class Liste { public static void main(string[] args) { Personne schmidt, lagafe; TreeSet<Personne> ensemble = new TreeSet<Personne>(); out.println("ensemble vide? "+ensemble.isempty()); ensemble.add(lagafe = new Personne("LAGAFE", "Gaston", 23)); ensemble.add(new Personne("GUILLEMET", "Virgule", 13)); ensemble.add(schmidt = new Personne("SCHMIDT", "Jules", 30)); ensemble.add(new Personne("GUILLEMET", "Virgule", 13)); ensemble.add(new Personne("TALON", "Achille", 13)); afficheensemble(ensemble); if (ensemble.contains(schmidt)) ensemble.remove(schmidt); afficheensemble(ensemble); ensemble.add(lagafe); afficheensemble(ensemble); ensemble.clear(); out.println("taille de ensemble : "+ensemble.size()); public static void afficheensemble(treeset<personne> ensemble) { out.print("ensemble = { "); for(personne valeur : ensemble) out.println(valeur+" "); out.println(" : nombre = "+ensemble.size()); class Personne { private String nom, prénom; private int âge; public Personne(String nom, String prénom, int âge) { this.nom = nom; this.prénom = prénom; this.âge = âge; public String getnom() { return nom; public String getprénom() { return prénom; public int getâge() { return âge; Le but de l'interface Comparable est justement là pour spécifier les critères de tri des classes que nous construisons. Cette interface déclare une seule méthode, compareto(). int compareto(t autre) L'interface java.lang.comparable<t> Compare cet objet avec un autre objet et renvoie une valeur négative si this est inférieur à autre, nulle si les deux objets sont identiques au sens de l'ordre de tri et positive si this est supérieur à autre. L'appel a.compareto(b) doit renvoyer 0 si a et b sont égaux, un nombre entier négatif si a est inférieur à b (selon l'ordre de tri choisi), et un nombre entier positif si a est supérieur à b. La valeur exacte renvoyée par cette méthode n'a pas grande importance. Seul son signe (>0, 0 ou <0) a une signification. Plusieurs classes de la plate-forme standard implémentent l'interface Comparable. La classe String en est un bon exemple. Sa méthode compareto() compare les chaînes selon l'ordre du dictionnaire (aussi appelé lexicographique). Si vous insérez vos propres objets, il vous faut définir un ordre de tri en implémentant l'interface Comparable. Il n'existe aucune implémentation par défaut de compareto() dans la classe Object. import java.util.*; import static java.lang.system.*; public class Liste { init: deps-jar: Résultat du programme

24 public static void main(string[] args) { Personne schmidt, lagafe; TreeSet<Personne> ensemble = new TreeSet<Personne>(); out.println("ensemble vide? "+ensemble.isempty()); ensemble.add(lagafe = new Personne("LAGAFE", "Gaston", 23)); ensemble.add(new Personne("GUILLEMET", "Virgule", 13)); ensemble.add(schmidt = new Personne("SCHMIDT", "Jules", 30)); ensemble.add(new Personne("GUILLEMET", "Virgule", 13)); ensemble.add(new Personne("TALON", "Achille", 13)); afficheensemble(ensemble); if (ensemble.contains(schmidt)) ensemble.remove(schmidt); afficheensemble(ensemble); ensemble.add(lagafe); afficheensemble(ensemble); ensemble.clear(); out.println("taille de ensemble : "+ensemble.size()); public static void afficheensemble(treeset<personne> ensemble) { out.println("ensemble { "); for(personne p : ensemble) out.println("..."+p.getnom()+" "+p.getprénom()+" : "+p.getâge()); out.println(" : nombre = "+ensemble.size()); class Personne implements Comparable<Personne> { private String nom, prénom; private int âge; public Personne(String nom, String prénom, int âge) { this.nom = nom; this.prénom = prénom; this.âge = âge; public String getnom() { return nom; public String getprénom() { return prénom; public int getâge() { return âge; Compiling 1 source file to C:\netbeans-5.0\travail \Liste\build\classes compile: run: ensemble vide? true ensemble {...GUILLEMET Virgule : 13...LAGAFE Gaston : 23...SCHMIDT Jules : 30...TALON Achille : 13 : nombre = 4 ensemble {...GUILLEMET Virgule : 13...LAGAFE Gaston : 23...TALON Achille : 13 : nombre = 3 ensemble {...GUILLEMET Virgule : 13...LAGAFE Gaston : 23...TALON Achille : 13 : nombre = 3 Taille de ensemble : 0 BUILD SUCCESSFUL (total time: 0 seconds) public int compareto(personne p) { if (!nom.equalsignorecase(p.nom)) return nom.comparetoignorecase(p.nom); if (!prénom.equalsignorecase(p.prénom)) return prénom.comparetoignorecase(p.prénom); return âge - p.âge; Il est bien évident que l'utilisation de l'interface Comparable pour définir un ordre de tri possède certaines limitations. Cette interface ne peut être implémentée qu'une seule fois. Mais que peut-on faire pour trier un ensemble d'articles selon leur numéro de code dans une collection, et en fonction de leur description dans une autre? De plus que pouvez-vous faire pour trier des objets d'une classe dont le concepteur n'a pas pris le soin d'implémenter l'interface Comparable? Dans ces situations, il faut demander à la collection de recourir à une autre méthode de comparaison, en passant un objet qui implémente l'interface Comparator dans le constructeur TreeSet (ou MapSet). L'interface Comparator déclare une méthode compare(). int compare(t a, T b) L'interface java.util.comparator<t> Compare deux objets et renvoie une valeur négative si a est inférieur à b, nulle si les deux objets sont identiques au sens de l'ordre de tri et positive si a est supérieur à b. Comme la méthode compareto(), la méthode compare() renvoie une valeur entière négative si a et inférieure à b, nulle si a et b sont identiques, et positive dans le dernier cas. Reprenons le même exemple et remplaçons l'interface Comparable par l'interface Comparator : import java.util.*; import static java.lang.system.*; public class Liste { public static void main(string[] args) { Personne schmidt, lagafe; TreeSet<Personne> ensemble = new TreeSet<Personne>(new TrierPersonne()); out.println("ensemble vide? "+ensemble.isempty()); ensemble.add(lagafe = new Personne("LAGAFE", "Gaston", 23)); ensemble.add(new Personne("GUILLEMET", "Virgule", 13)); ensemble.add(schmidt = new Personne("SCHMIDT", "Jules", 30)); ensemble.add(new Personne("GUILLEMET", "Virgule", 13)); ensemble.add(new Personne("TALON", "Achille", 13)); afficheensemble(ensemble); if (ensemble.contains(schmidt)) ensemble.remove(schmidt); afficheensemble(ensemble); ensemble.add(lagafe); afficheensemble(ensemble); ensemble.clear(); out.println("taille de ensemble : "+ensemble.size()); public static void afficheensemble(treeset<personne> ensemble) { out.println("ensemble { "); for(personne p : ensemble) out.println("..."+p.getnom()+" "+p.getprénom()+" : "+p.getâge()); out.println(" : nombre = "+ensemble.size()); class Personne { Résultat du programme init: deps-jar: Compiling 1 source file to C:\netbeans-5.0\travail \Liste\build\classes compile: run: ensemble vide? true ensemble {...GUILLEMET Virgule : 13...LAGAFE Gaston : 23...SCHMIDT Jules : 30...TALON Achille : 13 : nombre = 4 ensemble {...GUILLEMET Virgule : 13...LAGAFE Gaston : 23...TALON Achille : 13 : nombre = 3 ensemble {...GUILLEMET Virgule : 13...LAGAFE Gaston : 23...TALON Achille : 13 : nombre = 3

25 private String nom, prénom; private int âge; public Personne(String nom, String prénom, int âge) { this.nom = nom; this.prénom = prénom; this.âge = âge; public String getnom() { return nom; public String getprénom() { return prénom; public int getâge() { return âge; Taille de ensemble : 0 BUILD SUCCESSFUL (total time: 0 seconds) class TrierPersonne implements Comparator<Personne> { public int compare(personne p1, Personne p2) { if (!p1.getnom().equalsignorecase(p2.getnom())) return p1.getnom().comparetoignorecase(p2.getnom()); if (!p1.getprénom().equalsignorecase(p2.getprénom())) return p1.getprénom().comparetoignorecase(p2.getprénom()); return p1.getâge() - p2.getâge(); Remarquons au passage que l'objet qui représente l'implémentation de Comparator est passé au constructeur de l'arbre. Si vous construisez un arbre avec un comparateur, il se sert de cet objet même s'il doit comparer deux éléments. Comparator<? super E> comparator() L'interface java.util.sortedset<e> Renvoie le comparateur utilisé pour trier les éléments ou null si les éléments sont comparés avec la méthode compareto() de l'interface Comparable. E first() E last() Renvoie le plus grand ou le plus petit élément de la collection triée. Les ensembles de type EnumSet EnumSet est une implémentation efficace d'un ensemble, dont les éléments appartiennent à un type énuméré. Un type énuméré ayant un nombre fini d'instances, EnumSet est implémenté en interne sous la forme d'une suite de bits. Un bit est activé si la valeur correspondante est présente dans l'ensemble. static <E extends Enum<E>> EnumSet<E> allof(class<e> typeenumération) Renvoie un ensemble contenant toutes les valeurs du type énuméré donné. static <E extends Enum<E>> EnumSet<E> noneof(class<e> typeenumération) Renvoie un ensemble vide capable de contenir des valeurs du type énuméré donné. static <E extends Enum<E>> EnumSet<E> range(e de, E à) Renvoie un ensemble contenant toutes les valeurs comprises entre de et à (compris). static <E extends Enum<E>> EnumSet<E> of(e valeur) static <E extends Enum<E>> EnumSet<E> of(e valeur, E... valeurs) Renvoie un ensemble contenant les valeurs données. Il peut y en avoir un nombre quelconque. La classe java.util.enumset<e extends Enum<E>> La classe EnumSet ne possède pas de constructeurs publics. Utilisez une méthode de fabrique statique (qui génère une instance appropriée) pour construire l'ensemble désiré : enum Semaine {LUNDI, MARDI, MERCREDI, JEUDI, VENDREDI, SAMEDI, DIMANCHE; EnumSet<Semaine> touslesjours = EnumSet.allOf(Semaine.class); EnumSet<Semaine> aucunjours = EnumSet.noneOf(Semaine.class); EnumSet<Semaine> jourstravail = EnumSet.range(Semaine.LUNDI, Semaine.VENDREDI); EnumSet<Semaine> weekend = EnumSet.of(Semaine.SAMEDI, Semaine.DIMANCHE); Pour modifier un EnumSet, vous pouvez utiliser les méthodes habituelles de l'interface Set. Pour en savoir plus sur les énumérations. Exemple d'application d'un ensemble qui enregistre les jours de la semaine A titre d'exemple, nous allons mettre en oeuvre un ensemble qui stoque les jours de la semaine. La zone d'édition est remplacée par une boîte combo avec la liste des jours qu'il suffit alors de sélectionner. Remarquez au passage qu'un ensemble d'énumération est systématiquement ordonné. L'ordre proposé correspond à la suite des indices donné par chaque énumérateur. package ensemble; import java.awt.*; import java.awt.event.actionevent; import javax.swing.*; import java.util.*;

26 public class Semaine extends JFrame { private JCheckBox existe = new JCheckBox("Existe"); private Résultat liste = new Résultat(); private JPanel panneau = new JPanel(); private JToolBar barre = new JToolBar(); private JTextField nombre = new JTextField("0"); private enum Jours {Lundi, Mardi, Mercredi, Jeudi, Vendredi, Samedi, Dimanche; private JComboBox nouveaujour = new JComboBox(Jours.values()); private EnumSet<Jours> jours = EnumSet.noneOf(Jours.class); public Semaine() { super("les jours de la semaine"); nombre.seteditable(false); nombre.sethorizontalalignment(jlabel.center); Action ajouter = new AbstractAction("Ajout") { jours.add((jours) nouveaujour.getselecteditem()); analysejours(); ; barre.add(ajouter); barre.add(new AbstractAction("Suppression") { jours.remove((jours) nouveaujour.getselecteditem()); analysejours(); ); barre.add(new AbstractAction("Tout effacer") { jours.clear(); analysejours(); ); barre.add(new AbstractAction("Existe?") { existe.setselected(jours.contains((jours) nouveaujour.getselecteditem())); ); barre.addseparator(); barre.add(new JLabel("Nombre : ")); barre.add(nombre); add(barre, BorderLayout.NORTH); nouveaujour.addactionlistener(ajouter); panneau.add(nouveaujour); panneau.add(existe); add(panneau); add(liste, BorderLayout.SOUTH); setsize(400, 135); setlocationrelativeto(null); setresizable(false); setdefaultcloseoperation(exit_on_close); setvisible(true); public void analysejours() { liste.repaint(); nombre.settext(""+jours.size()); existe.setselected(jours.contains((jours) nouveaujour.getselecteditem())); public static void main(string[] args) { new Semaine(); private class Résultat extends JLabel { public Résultat() { setforeground(color.red); public void paint(graphics g) { settext(jours.tostring()); super.paint(g); Les ensembles de type LinkedHashSet Il reste un dernier ensemble, un peu particulier, qui se souvient de l'ordre d'insertion des éléments. De cette manière, vous évitez l'ordre assez aléatoire des éléments dans la table de hachage. A mesure que des entrées sont insérées dans la table, elles sont simplement reliées ensemble dans une liste doublement chaînée. Cet ensemble est implémenté par la classe LinkedHashSet. Exemple d'application d'un ensemble de prénoms Nous allons tout de suite prendre un exemple puisque cette collection ne présente rien de bien nouveau si ce n'est sa petite particularité. Je vais reprendre l'application précédente sur la gestion des prénoms et placer une collection de type LinkedHashSet à la place de HashSet. Comme toujours, dans les ensembles, un prénom doit être exclusif. Remarquez bien que cette fois-ci les prénoms se place dans la collection suivant l'ordre d'insertion.

27 Vu que nous utilisons comme éléments de collection des chaînes de caractères, il n'est pas nécessaire de se préoccuper des méthodes equals() et hashcode(). package ensemble; import java.awt.*; import java.awt.event.actionevent; import javax.swing.*; import java.util.*; public class Prénoms extends JFrame { private JTextField nouveauprénom = new JTextField("Nouveau prénom", 18); private JCheckBox existe = new JCheckBox("Existe"); private Résultat liste = new Résultat(); private JPanel panneau = new JPanel(); private JToolBar barre = new JToolBar(); private JTextField nombre = new JTextField("0"); private LinkedHashSet<String> prénoms = new LinkedHashSet<String>(); public Prénoms() { super("ensemble de prénoms"); nombre.seteditable(false); nombre.sethorizontalalignment(jlabel.center); Action ajouter = new AbstractAction("Ajout") { prénoms.add(nouveauprénom.gettext()); ; barre.add(ajouter); barre.add(new AbstractAction("Suppression") { prénoms.remove(nouveauprénom.gettext()); ); barre.add(new AbstractAction("Tout effacer") { prénoms.clear(); ); barre.add(new AbstractAction("Existe?") { existe.setselected(prénoms.contains(nouveauprénom.gettext())); ); barre.addseparator(); barre.add(new JLabel("Nombre : ")); barre.add(nombre); add(barre, BorderLayout.NORTH); nouveauprénom.addactionlistener(ajouter); panneau.add(nouveauprénom); panneau.add(existe); add(panneau); add(liste, BorderLayout.SOUTH); setsize(470, 135); setlocationrelativeto(null); setresizable(false); setdefaultcloseoperation(exit_on_close); setvisible(true); public void analyseprénoms() { liste.repaint(); nombre.settext(""+prénoms.size()); existe.setselected(prénoms.contains(nouveauprénom.gettext())); public static void main(string[] args) { new Prénoms(); private class Résultat extends JLabel { public Résultat() { setforeground(color.red); public void paint(graphics g) { settext(prénoms.tostring()); super.paint(g); Les queues- ArrayDeque, LinkedList et PriorityQueue

28 Les queues implémentent les files d'attente. Les queues sont utilisées si vous devez enregistrer des objets et les récupérer selon la règle "premier entré, premier sortie" (FIFO - First In First Out) Il en existe de plusieurs sortes : 1. Les queues à simple entrées, 2. Les queues à doubles entrées, 3. et les queues de priorité qui récupèrent des éléments triés. Interface de queue de données - Queue Comme nous l'avons déjà découvert au tout début de l'étude des collections, il existe une interface Queue, dérivée de l'interface Collection, destinée à la gestion des files d'attente (ou queues). Il s'agit d'une structure dans laquelle nous pouvons : 1. Introduire un nouvel élément à la fin de la queue, si la queue n'est pas pleine : L'introduction d'un nouvel élément dans la queue se fait à l'aide d'une nouvelle méthode offer(), qui présente, sur la méthode add() de l'interface Collection, l'avantage de ne pas déclencher d'exception quand la queue est pleine ; dans ce cas, offer() renvoie simplement la valeur false. 2. Prélever le premier élément de la queue (début de queue) : Le prélèvement du premier élément de la queue peut se faire : de façon destructive : à l'aide de la méthode poll() : l'élément ainsi prélevé est supprimé de la queue ; la méthode renvoie null si la queue est vide. de façon non destructive : à l'aide de la méthode peek(). 3. Déterminer le nombre d'éléments contenus dans une queue. Il est impossible d'ajouter ou de supprimer des éléments au milieu de la file d'attente. L'interface java.util.queue<e> boolean add(e élément) boolean offer(e élément) Ajoute l'élément à la fin de la queue et renvoie true, à condition que la queue ne soit pas pleine. Si la queue est pleine, la première méthode déclenche une exception IllegalStateException, tandis que la seconde renvoie false. E remove() E poll() Suppriment et renvoient l'élément au début de cette queue, à condition que la queue ne soit pas vide. Si la queue est vide, la première méthode déclenche une exception NoSuchElementException, tandis que la seconde renvoie null. E element() E peek() Renvoient l'élément au début de cette queue sans le supprimer, à condition que la queue ne soit pas vide. Si la queue est vide, la première méthode déclenche une exception NoSuchElementException, tandis que la seconde renvoie null. Interface de queue à double entrée - Deque Une queue à deux extrémités permet d'ajouter ou de supprimer efficacement des éléments en début et à la fin. Java 6 a introduit une nouvelle interface Deque, dérivée de Queue, destinée à gérer les files d'attente à double entrée, c'est-à-dire dans lesquelles nous pouvons réaliser l'une des opérations suivantes à l'une des extrémités de la queue : 1. ajouter un élément, 2. examiner un élément, 3. supprimer un élément. Il est impossible d'ajouter ou de supprimer des éléments au milieu de la file d'attente. Pour chacune de ces possibilités (3 actions, 2 extrémités), il existe deux méthodes : 1. l'une déclenchant une exception quand l'opération échoue (pile pleine ou vide, selon le cas), 2. l'autre renvoyant une valeur particulière (null pour une méthode de prélèvement ou d'examen, false pour une méthode d'ajout). A noter que les méthodes de l'interface Queue restent utilisables, sachant que celles d'ajout agissent sur la queue, tandis que celles d'examen ou de suppression agissent sur la tête. void addfirst(e élément) void addlast(e élément) boolean offerfirst(e élément) boolean offerlast(e élément) L'interface java.util.deque<e> Ajoute l'élément au début ou à la fin de la queue. Si la queue est pleine, les deux premières méthodes déclenchent une exception IllegalStateException, tandis que les deux dernières renvoient false. E removefirst() E removelast()

29 E pollfirst() E polllast() Suppriment et renvoient l'élément au début ou à la fin de cette queue, à condition que la queue ne soit pas vide. Si la queue est vide, les deux premières méthodes déclenchent une exception NoSuchElementException, tandis que les deux dernières méthodes renvoient null. E getfirst() E getlast() E peekfirst() E peeklast() Renvoient l'élément au début ou à la fin de cette queue sans le supprimer, à condition que la queue ne soit pas vide. Si la queue est vide, les deux premières méthodes déclenchent une exception NoSuchElementException, tandis que les deux dernières méthodes renvoient null. Les classes qui implémentent ces deux interfaces spécifiques aux files d'attente Il existe trois classes qui sont capables d'implémenter l'une de ces interface : 1. La première implémentation, que nous connaissons bien, est représentée par la classe LinkedList, se sert d'une liste chaînée : Cette classe LinkedList a été modifiée par le Java 5, pour y intégrer les nouvelles méthodes de l'interface Queue. Depuis Java 6, cette classe implémente également l'interface Deque disposant de méthodes d'action à la fois sur le début et la fin de la liste. 2. La deuxième implémentation, représentée par la classe ArrayDeque, se sert d'un tableau circulaire : Comme son non l'indique, la classe ArrayDeque implément l'interface Deque. Il s'agit donc d'une implémentation d'une queue à double entrée sous forme d'un tableau (éléments contigus en mémoire) redimensionnable à volonté (comme l'est l'arraylist). On notera bien que, malgré son nom, cette classe n'est pas destinée à concurencer ArrayList, car elle ne dispose pas d'opérateur d'accès direct à un élément. Il s'agit simplement d'une impémentation plus efficace que LinkedList pour une queue à double entrée. Pourquoi choisir une implémentation plutôt qu'une autre? Une interface ne fournit aucun détail quant à l'efficacité de son implémentation. De manière générale, un tableau circulaire est plus efficace qu'une liste chaînée, et il est donc préférable à cette dernière. Toutefois, comme d'habitude, il y a un prix à payer. Les tableaux circulaires font partie d'une collection bornée, qui a une capacité déterminée. Si vous ne connaissez pas la limite maximale du nombre d'objets stockés par votre programme, vous préférerez probablement une implémentation en liste chaînée. En étudiant la documentation de l'api, vous découvrirez un autre jeu de classes dont le nom commence par Abstract, comme AbstractQueue. Ces classes sont destinées aux implémentations de bibliothèque. Pour implémenter votre propre classe de queue, il sera plus facile d'étendre AbstractQueue que d'implémenter toutes les méthodes de l'interface Queue. ArrayDeque() ArrayDeque(int capacité) Construisent des queues sans limites avec une capacité initiale de 16 ou la capacité initiale donnée. La classe java.util.arraydeque<e> Exemple d'application d'une file d'attente de prénoms Pour éviter de mettre en oeuvre une nouvelle application, je reprends l'application précédente pour gérer une simple queue de prénoms. La file d'attente est implémentée par la classe ArrayDeque mais je ne me sert que d'une queue à simple entrée. Cette fois-ci, lorsque que nous supprimons un prénom de la queue, elle s'affiche dans la zone d'édition. Remarquez bien que les prénoms se place dans la collection suivant l'ordre d'insertion. La suppression se fait uniquement sur les premiers entrés

30 ... public class Prénoms extends JFrame { private JTextField nouveauprénom = new JTextField("Nouveau prénom", 18); private JCheckBox existe = new JCheckBox("Existe"); private Résultat liste = new Résultat(); private JPanel panneau = new JPanel(); private JToolBar barre = new JToolBar(); private JTextField nombre = new JTextField("0"); private ArrayDeque<String> prénoms = new ArrayDeque<String>(); public Prénoms() { super("ensemble de prénoms"); nombre.seteditable(false); nombre.sethorizontalalignment(jlabel.center); Action ajouter = new AbstractAction("Ajout") { prénoms.offer(nouveauprénom.gettext()); ; barre.add(ajouter); barre.add(new AbstractAction("Suppression") { nouveauprénom.settext(prénoms.poll()); ); barre.add(new AbstractAction("Tout effacer") { prénoms.clear(); ); barre.add(new AbstractAction("Existe?") { existe.setselected(prénoms.contains(nouveauprénom.gettext())); ); La troisième implémentation est représentée par la classe PriorityQueue, introduite par Java 5, qui permet de choisir une relation d'ordre ; dans ce cas, le type des éléments doit implémenter l'interface Comparable ou être doté d'un comparateur approprié. Les éléments de la queue sont alors ordonnés par cette relation d'ordre et le prélèvement d'un élément porte alors sur le "premier" au sens de cette relation (on parle du "plus prioritaire", d'où le nom de PriorityQueue). Une queue de priorité récupère des éléments triés, après qu'ils ont été insérés dans un ordre arbitraire. Ainsi, dès que vous appelez la méthode remove (), vous obtenez le plus petit élément se trouvant dans la queue. Celle-ci ne trie toutefois pas tous ses éléments. Si vous parcourez les éléments, ils ne sont pas nécessairement triés. La queue de priorité fait usage d'une structure de données efficace et élégante, appelée un tas. Il s'agit d'une arborescence binaire dans laquelle les opérations add() et remove() amènent l'élément le plus petit à graviter autour de la racine, sans perdre de temps à trier tous les éléments. A l'instar d'un TreeSet, une queue de priorité peut contenir soit les éléments d'une classe qui implémente l'interface Comparable, soit un objet Comparator que vous fournissez dans le constructeur. Ces queues servent généralement à planifier les tâches. Chaque tâche se voit attribuer une priorité et elles sont ajoutées dans un ordre aléatoire. Dès le démarrage possible d'une nouvelle tâche, celle ayant la plus haute priorité est supprimée de la queue (la priorité 1 étant généralement la plus forte, l'opération de suppression concerne le plus petit élément). PriorityQueue() PriorityQueue(int capacité) Construisent une queue de priorité pour stocker des objets Comparable. PriorityQueue(int capacité, Comparator<? super E> comparateur) Construit une queue de priorité et utilise le comparateur spécifié pour trier ses éléments. La classe java.util.piorityqueue<e> Exemple d'application d'une file d'attente de prénoms Je reprends de nouveau l'application sur la file d'attente des prénoms. Cette fois-ci toutefois, la file d'attente est implémentée par la classe PriorityQueue. Ainsi, lorsque que nous supprimons un prénom de la queue, il s'agit toujours de celui qui est le premier dans l'ordre alphabétique. Au moment de l'insertion, tous les prénoms ne sont pas spécialement dans l'ordre alphabétique, sauf pour le premier. Lorsque nous commençons à supprimer des éléments, un arrangement s'effectue pour être sûr que le suivant soit bien le nouveau premier dans l'ordre alphabétique.

31 ... public class Prénoms extends JFrame { private JTextField nouveauprénom = new JTextField("Nouveau prénom", 18); private JCheckBox existe = new JCheckBox("Existe"); private Résultat liste = new Résultat(); private JPanel panneau = new JPanel(); private JToolBar barre = new JToolBar(); private JTextField nombre = new JTextField("0"); private PriorityQueue<String> prénoms = new PriorityQueue<String>(); public Prénoms() { super("ensemble de prénoms"); nombre.seteditable(false); nombre.sethorizontalalignment(jlabel.center); Action ajouter = new AbstractAction("Ajout") { prénoms.offer(nouveauprénom.gettext()); ; barre.add(ajouter); barre.add(new AbstractAction("Suppression") { nouveauprénom.settext(prénoms.poll()); ); barre.add(new AbstractAction("Tout effacer") { prénoms.clear(); ); barre.add(new AbstractAction("Existe?") { existe.setselected(prénoms.contains(nouveauprénom.gettext())); );... Dictionnaires - Cartes - Tables associatives Un Set (ensemble) est une collection qui vous permet de retrouver rapidement un élément. Pour cela, il faut le connaître exactement, ce qui n'est pas souvent le cas. En fait, nous disposons généralement de certaines informations importantes sur l'élément à rechercher, et il faut le retrouver à partir de ces informations. La structure de données cartes (ou map) permet d'effectuer ce genre de recherche. Une carte enregistre des paires clé / valeur. Une valeur peut être retrouvée à partir de la clé correspondante. Les cartes sont également appelées table associative, ou dictionnaire. Une table associative permet de conserver une information associant deux parties nommées clé et valeur. Elle est principalement destinée à renvoyer la valeur associée à une clé donnée. Les exemples les plus caractéristiques sont : 1. Le dictionnaire : à un mot (clé), nous associons une valeur qui est sa définition. 2. L'annuaire téléphonique usuel : à un nom (clé), nous associons une valeur comportant le numéro de téléphone et, éventuellement, une adresse. 3. L'annuaire inversé : à un numéro de téléphone (qui devient la clé), nous associons une valeur comportant le nom et éventuellement, une adresse. On notera que les ensembles (Set) déjà étudiés sont des cas particuliers de telles tables, dans lesquels la valeur serait vide. La bibliothèque Java propose deux implémentations générales des cartes : HashMap et TreeMap (Une HashMap répartit les clés dans plusieurs catégories, alors que la TreeMap se sert d'un ordre total pour organiser les clés dans un arbre de recherche). Ces deux classes gèrent des collections d'éléments accessibles par une clé correspondant (map en anglais) à un élément. Ces classes mémorisent en fait un ensemble d'entrées (entries) associant une clé et son élément (key-value). L'accès par clé dans une collection est comparable à l'accès par indice dans un tableau. Comme les clés peuvent être des chaînes de caractères, des instances des classes d'emballage ou d'autres classes, les classes HashMap et TreeMap permettent d'accéder à ces ensembles d'éléments de manière plus élaborée qu'avec un indice entier. Attention, les clés doivent être uniques. Il n'est également pas permis d'associer deux valeurs à une même clé. Ainsi, par exemple, il n'est pas possible d'avoir plusieurs numéros de téléphone pour une même personne. Il existe toutefois des solutions pour résoudre cet apparente difficulté. Implémentation Comme pour les ensembles, l'intérêt des tables associatives est de pouvoir retrouver rapidement une clé donnée pour en obtenir l'information associée. Java dispose de plusieurs classes, avec les deux que nous venons de découvrir, qui implémentent ces différents dictionnaires, chacune ayant sa propre particularité. Dans tous les cas, seule la clé sera utilisée pour ordonnancer les informations.

32 Comme nous l'avons déjà signalé, toutes ces classes n'implémentent plus l'interface Collection mais une autre interface nommée Map. Ceci provient essentiellement du fait que leurs éléments ne sont plus à proprement parler des objets mais des "paires" d'objets, c'est-à-dire une association entre deux objets. La classe HashMap mémorise ses entrées dans un ordre quelconque, tandis que la classe TreeMap mémorise ses entrées dans l'ordre ascendant avec un arbre, pour maintenir plus rapidement le tri des éléments stockées. Vous avez ci-dessous d'autres classes qui implémentent également l'interface Map. 1. HashMap : Carte de hachage : Une structure de données en forme de table de hachage qui stocke les associations clé / valeur. Cette implémentation se sert d'un code de hachage pour les objets formant les clés. HashMap() HashMap(int capacité) HashMap(int capacité, float facteurdecharge) La classe java.util.hashmap<key, Value> Construisent une carte de hachage vide avec la capacité et le facteur de charge spécifiés (un nombre compris entre 0,0 et 1,0) qui détermine le pourcentage de remplissage au-dela duquel la table de hachage sera réorganisée). Le facteur de charge par défaut vaut 0, TreeMap : Carte triée : Une concordance dans laquelle les clés sont triées. Cette implémentation se sert d'un ordre total pour organiser les clés dans un arbre de recherche. Il s'agit donc d'un arbre binaire qui prend en compte l'ordre induit par la méthode compareto() ou par un comparateur fixé à la construction. Les fonctions de hachage ou de comparaison ne sont appliquées qu'aux clés. Les valeurs associées ne sont ni comparées ni réparties. TreeMap(Comparator<? super Key> carte) La classe java.util.treemap<key, Value> Construit une carte en arbre et se sert du comparateur pour trier ses clés. TreeMap(Map<? extends Key,? extends Value> entrées) Construit une carte en arbre et y ajoute toutes les entrées de la carte spécifiée. TreeMap(SortedMap<? extends Key,? extends Value> entrées) Construit un arbre, ajoute toutes les entrées de la carte triée et se sert du même comparateur d'éléments que la carte triée. Comparator<? super Key> comparator() L'interface java.util.sortedmap<key, Value> Renvoie le comparateur servant à trier les clés ou null si les clés sont comparées à l'aide de la méthode compareto() de l'interface Comparable. Key firstkey() Key lastkey() Renvoie la plus petite ou la plus grande clé de la carte. 3. EnumMap : Carte d'énumération : Une concordance dans laquelle les clés appartiennent à un type énuméré. Il est implémenté de manière simple et efficace sous forme de tableau de valeurs. Le type de la clé doit être spécifié dans le constructeur : enum Semaine {LUNDI, MARDI, MERCREDI, JEUDI, VENDREDI, SAMEDI, DIMANCHE; EnumMap<Semaine, String> alertes = new EnumMap<Semaine, String>(Semaine.class); EnumMap(Class<Key> typedeclé) La classe java.util.enummap<key extends <Key>, Value> Construit une carte vide dont les clés ont le type spécifié. 4. LinkedHashMap : Carte de hachage liée : Une concordance qui se souvient de l'ordre d'ajout des entrées. Une carte de hachage liée peut utiliser l'ordre d'accès, et non l'ordre d'insertion, pour parcourir les entrées de la carte. Chaque fois que vous appelez get() et put(), l'entrée affectée est supprimée de sa position et placée à la fin de la liste chaînée des entrées (seule la position dans la liste chaînée est concernée, et non le seau de la table de hachage. Une entrée reste toujours dans le seau qui correspond au code de hachage de la clé). LinkedHashMap() LinkedHashMap(int capacité) LinkedHashMap(int capacité, float facteurdecharge) LinkedHashMap(int capacité, float facteurdecharge, boolean ordred'accès) La classe java.util.linkedhashmap<key, Value> Construisent une carte de hachage liée vide avec la capacité, le facteur de charge et l'ordre spécifiés. Le paramètre ordred'accès vaut true pour l'ordre d'accès, false pour l'ordre d'insertion. protected boolean removeedestentry(map.entry<key, Value> ancien) Doit être surchargé pour renvoyer true si vous souhaitez supprimer l'entrée la plus ancienne. Le paramètre le plus ancien correspond à l'entrée dont on envisage la suppression. Cette méthode est appelée après qu'une entrée a été ajoutée à la carte. L'implémentation par défaut renvoie false, les anciens éléments ne sont pas supprimés par défaut. Vous pouvez toutefois redéfinir cette méthode pour renvoyer de manière sélective true, par exemple si l'entrée la plus ancienne répond à une certaine condition ou si la carte dépasse une certaine taille. L'ordre d'accès est utile pour implémenter le système de "la dernière utilisée" pour un cache. Par exemple, vous voulez conserver les entrées fréquemment lues en mémoire et lire des objets lus moins souvent à partir d'une base de données. Lorsqu'une entrée d'une table est introuvable et que la table est déjà assez pleine, vous pouvez obtenir un itérateur dans la table, puis supprimer les premiers éléments qu'elle énumère. Ces entrées étaient les dernières utilisées. Vous pouvez même automatiser cette procédure. Formez une sous-classe de LinkedHashMap et surcharchez la méthode removeeldestentry(). Ajoutez ensuite une nouvelle entrée pour supprimer l'entrée la plus ancienne lorsque votre méthode renvoie true. Par exemple, le cache suivant est conservé à une taille maximale de 100 éléments :

33 Map<Key, Value> cache = new LinkedHashMap<Key, Value>(64, 0.75F, true) { protected boolean removeedestentry(map.entry<key, Value> ancien) { return size() > 100; Vous pouvez également envisager de prendre en compte l'élément ancien pour décider si vous devez le supprimer. Par exemple, vous devrez peut-être vérifier une indication de la date et de l'heure stockée avec l'entrée. 5. WeakHashMap : Carte de hachage faible : Une concordance avec des valeurs pouvant être réclamées par le ramasse-miettes si elles ne sont pas utilisées ailleurs. La classe WeakHashMap a été conçue dans le but de résoudre un problème intéressant. Que se passe-t-il avec une valeur lorsque sa clé n'est plus utilisée nulle part dans le programme? Supposons que la dernière référence à une clé ait disparu. Il n'y a alors plus aucune manière de se référer à l'objet de la valeur. Toutefois, étant donné qu'aucune partie du programme ne possède plus la clé, la paire clé/valeur ne peut être supprimée de la carte. Pourquoi alors le ramasse-miettes ne peut-t-il pas la supprimer? Ce serait en effet son rôle de supprimer les objets inutilisés. Malheureusement, la situation n'est pas aussi simple. Le ramasse-miettes suit les objets vivants. Tant que l'objet de carte est vivant, tous les seaux qu'il contient sont également vivants et ils ne seront pas réclamés. Ainsi, votre programme doit supprimer les valeurs inutilisées des cartes vivantes. Vous pouvez également utiliser à la place WeakHashMap. Cette structure de données coopère avec le ramasse-miettes pour supprimer les paires clé/valeur lorsque la seule référence à la clé est la référence de l'entrée de la table de hachage. Voici en fait le fonctionnement interne de ce mécanisme. WeakHashMap utilise des références faibles pour conserver les clés. Un objet WeakReference contient une référence à un autre objet, dans notre cas, une clé de table de hachage. Les objets de ce type sont considérés de manière particulière par le ramasse-miettes. Normalement, s'il découvre qu'un objet particulier ne possède pas de références vers lui, il réclame simplement l'objet. Toutefois, si l'objet peut être atteint uniquement par un WeakReference, le ramasse-miettes réclame toujours l'objet, mais place la référence faible qui y menait dans une queue. Les opérations du WeakHashMap vérifient périodiquement cette queue pour y retrouver des références faibles nouvellement arrivées. L'arrivée d'une référence faible dans la queue signifie que la clé n'est plus utilisée par personne et qu'elle a été collectée. WeakHashMap supprime alors l'entrée associée. WeakHashMap() WeakHashMap(int capacité) WeakHashMap(int capacité, float facteurdecharge) Construisent une carte de hachage vide avec la capacité et le facteur de charge spécifiés. La classe java.util.weakhashmap<key, Value> 6. IdentityHashMap : Carte de hachage d'identité : Une concordance avec des clés comparées par ==, et non par equals(). Java 1.4 a ajouté une autre classe IdentyHashMap pour un autre objectif assez spécialisé, où les valeurs de hachage pour les clés ne doivent pas être calculées par la méthode hashcode() mais par la méthode System.identityHashCode(). C'est la méthode utilisée par Object.hashCode() pour calculer le code de hachage à partir de l'adresse mémoire de l'objet. De même, pour la comparaison des objets, IdentityHashMap utilise ==, et non equals(). En d'autres termes, différents objets de clé sont considérés comme distincts, même s'ils ont un contenu égal. Cette classe est utile pour implémenter des algorithmes object transversal (comme la sérialisation des objets), dans lesquels vous souhaitez effectuer le suivi des objets auxquels on a déjà appliqué traversed. WeakHashMap() WeakHashMap(int capacité) La classe java.util.identityhashmap<key, Value> Construisent une carte de hachage d'identité vide dont la capacité est la plus petite puissance de 2 dépassant 1,5 x capacité (le paramètre par défaut pour capacité vaut 21). static int identityhashcode(object objet) La classe java.lang.system Renvoie le même code de hachage (dérivée de l'adresse mémoire de l'objet) que Object.hashCode(), même si la classe à laquelle appartient objet a redéfini la méthode hashcode(). Fonctionnalités des cartes Toutes les classes que nous venons de décourvrir implémentent l'interface Map qui proposent des fonctionnalités communes et que nous allons maintenant étudier. 1. Ajout d'information : La plupart des constructeurs créent une table vide. Pour ajouter une clé à une carte, nous devons utiliser la méthode put() à laquelle nous fournissons la clé et la valeur associée. Map<String, String> téléphones = new TreeMap<String, String>(); téléphones.put("virgule GUILLEMET", " "); Les clés doivent être uniques. Il n'est pas permis d'associer deux valeurs à la même clé. Si vous appelez la méthode put() deux fois avec la même clé, la seconde valeur remplace la première. En fait, put() renvoie la dernière valeur correspondant à la clé fournie. Notez que, comme pour les autres collections, les clés et les valeurs doivent être des objets. Il n'est théoriquement pas nécessaire que toutes les clés soient du même type, pas plus que les éléments. En pratique, ce sera presque toujours le cas pour des questions évidentes de facilité d'exploitation de la table. 2. Recherche d'information : Pour retrouver un élément, il faut se servir de la clé (et par conséquent, il faut la connaître). Nous obtenons la valeur à une clé donnée à l'aide de la méthode get() : String nom = "Virgule GUILLEMET"; String téléphone = téléphones.get(nom); il (téléphone == null) System.out.println("Aucun numéro de téléphone"); else System.out.println("Le numéro de "+nom+" est "+téléphone); Si aucune information n'est stockée dans la carte pour une clé spécifiée, get() renvoie null.

34 La méthode containskey() permet de savoir si une clé est présente dans la carte. De la même façon, il est possible de contrôler si une valeur existe dans la carte au moyen de la méthode containsvalue(). 3. Suppression d'information : Nous pouvons supprimer un élément d'une carte, ayant une clé donnée, en utilisant la méthode remove(), laquelle fournit en retour l'ancienne valeur associée si la clé existe ou la valeur null dans le cas contraire. String anciennuméro = téléphones.remove(nom); il (anciennuméro == null) System.out.println("Ce nom "+nom+" n'existe pas"); else System.out.println("Le numéro de "+anciennuméro+" est supprimé"); 4. Méthodes classiques : Comme pour toutes les autres collections, nous retrouvons les méthodes classiques : clear(), isempty(), size(). Value get(key clé) Cherche la valeur associée à une clé, puis renvoie l'objet associé ou null si la clé n'a pas été trouvée dans la carte. La clé peut être nulle. Value put(key clé, Value valeur) L'interface java.util.map<key, Value> Associe une clé et une valeur dans une carte. Si la clé est déjà présente dans la carte, le nouvel objet remplace celui qui était associé à la clé. Cette méthode renvoie l'ancienne valeur associée à la clé ou null si la clé n'existait pas dans la carte. La clé peut être nulle, mais la valeur doit être non nulle. int size() Retourne le nombre de clé/valeur présentes dans la carte. Value remove(object clé) Supprime la valeur correspondante à la clé spécifiée (si elle est présente). boolean isempty() Indique si la carte est vide de tout élément clé/valeur. void clear() Enlève toutes les occurences clé/valeur de la carte. void putall(map<? extends Key,? extends Value>) Ajoute toutes les entrées d'une carte spécifiée à cette carte. boolean containskey(object clé) Renvoie true si la clé spécifiée est présente dans la carte. boolean containsvalue(object valeur) Renvoie true si la valeur spécifiée est présente dans la carte. Set<Map, Entry<Key, Value>> entryset() Renvoie une vue des objets Map.Entry, contenant les paires clé / valeur de la carte. Vous pouvez supprimer des éléments de cette vue (ils sont alors éliminés de la carte), mais vous ne pouvez pas en ajouter de nouveaux. Set<Key> keyset() Renvoie une vue de toutes les clés de la carte. Lorsque vous supprimez des éléments de cet ensemble, la clé et la valeur associée sont supprimées de la carte. En revanche, vous ne pouvez pas ajouter de nouveaux éléments. Collection<Value> values() Renvoie une vue de toutes les valeurs de la carte. Vous pouvez supprimer des éléments de cet ensemble (la clé et la valeur associée sont supprimées de la carte), mais vous n'avez pas le droit d'y ajouter de nouveaux éléments. Il est souvent plus confortable de déclarer la structure de données cartes au travers de l'interface Map<K, V> plutôt que d'utiliser directement les classes HashMap, TreeMap, etc. Effectivement, au travers de cette procédure, il est plus facile ensuite de récupérer chaque entrée séparément. Parcours d'une table - notion de vue Les cartes ne sont pas vraiment considérées comme des collections. Effectivement, les classes qui implémentent les cartes ne disposent pas d'itérateurs. Il reste néanmoins possible d'obtenir une vue d'une carte, c'est-à-dire un objet qui implémente l'interface Collection ou l'une de ses sous-interfaces. Il existe trois vues différentes : 1. Set<Key> keyset() : procure l'ensemble des clés, 2. Collection<Key> values() : délivre l'ensemble des valeurs, 3. Set<Map.Entry<Key, Value>> entryset() : L'ensemble des paires clé / valeur. Notons que la méthode keyset() ne délivre pas un HashSet ou un TreeSet, mais il s'agit d'un objet d'une autre classe qui implémente l'interface Set, laquelle étend l'interface Collection. Vous pouvez donc utiliser le résultat de cette méthode comme n'importe quelle autre collection. L'ensemble des clés et des paires clé / valeur forme un Set parce qu'il ne peut exister qu'un seul exemplaire d'une clé dans une carte. Les éléments de la troisième méthode font partie de la classe interne statique Map.Entry. Key getkey() Value getvalue() Renvoie la clé ou la valeur de cette entrée. Value setvalue(value valeur) Modifie la valeur de la carte associée avec la nouvelle valeur spécifiée et renvoie l'ancienne valeur. void putall(map<? extends Key,? extends Value>) Ajoute toutes les entrées d'une carte spécifiée à cette carte. L'interface java.util.map.entry<key, Value>

35 A l'aide de la méthode entryset(), nous pouvons donc facilement voir une carte comme un ensemble de "paire", une paire n'étant rien d'autre qu'un élément de type Map.Entry réunissant deux objets (de types a priori quelconques). Les méthodes getkey() et getvalue() de Map.Entry permettent d'extraire respectivement la clé et la valeur d'une paire. Notez que l'ensemble fourni par la méthode entryset() n'est pas une copie des informations figurant dans la carte. Il s'agit de ce que nous nommons une "vue". Si nous opérons des modifications dans la carte, elles seront directement perceptibles sur la vue associée. De plus si nous appliquons la méthode remove() à un élément courant (paire) de la vue, nous supprimons du même coup l'élément de la carte. En revanche, il n'est pas permis d'ajouter directement des éléments dans la vue elle-même. Manipuler la classe TreeMap à l'aide de l'interface NavigableMap Depuis Java 6, les tables de TreeMap implémentent également l'interface NavigableMap qui prévoit des méthodes exploitant l'ordre total induit sur les clés, par l'organisation en arbre binaire. Ces méthodes permettent de retrouver et, éventuellement, de supprimer des éléments correspondant à la plus petite ou la plus grande des clés, ou encore de trouver un élément ayant la clé la plus proche (avant ou après) d'une valeur donnée. Il est possible de parcourir les éléments dans l'ordre inverse de l'ordre naturel des clés. Enfin, nous pouvons obtenir une vue d'une partie du map, en se fondant sur la valeur d'une clé qui sert en quelque sorte de "délimiteur". Map.Entry<Key, Value> ceilingentry(key clé) Fournit la "paire" correspondant à la plus petite clé supérieure ou égale à clé (null si aucune). NavigableSet<Key> descendingkeyset() Fournit une vue de l'ensemble des clés dans l'ordre inverse. NavigableMap<Key, Value> descendingmap(key clé) Fournit une vue dans l'ordre inverse. Map.Entry<Key, Value> firstentry() Fournit la "paire" correspondant à la plus petite clé (null si la carte est vide). Map.Entry<Key, Value> floorentry(key clé) Fournit la "paire" correspondant à la plus grande clé inférieure ou égale à clé (null si aucune). Key floorkey(key clé) Fournit la plus grande clé inférieure ou égale à clé (null si aucune). SortedMap<Key, Value> headmap(key clémaxi) Fournit une vue formée des éléments dont la clé est inférieure à clémaxi. NavigableMap<Key, Value> headmap(key clémaxi, boolean inclus) Fournit une vue formée des éléments dont la clé est inférieure (ou égale si inclus est vrai) à clémaxi. Map.Entry<Key, Value> higherentry(key clé) Fournit la "paire" correspondant à la plus petit clé supérieure à clé (null si aucune). Key higherkey(key clé) Fournit la plus petit clé supérieure à clé (null si aucune). Map.Entry<Key, Value> lastentry() Fournit la "paire" correspondant à la plus grande clé (null si la carte est vide). Map.Entry<Key, Value> lowerentry(key clé) Fournit la "paire" correspondant à la grande clé inférieure à clé (null si aucune). Key lowerkey(key clé) Fournit la plus grande clé inférieure à clé (null si aucune). NavigableSet<Key> navigablekeyset() Fournit une vue de l'ensemble des clés dans l'ordre naturel. Map.Entry<Key, Value> pollfirstentry(key clé) Fournit, en la supprimant, la "paire" correspondant à la plus petite clé (null si la carte est vide). Map.Entry<Key, Value> polllastentry(key clé) Fournit, en la supprimant, la "paire" correspondant à la plus grande clé (null si la carte est vide). NavigableMap<Key, Value> submap(key clédébut, boolean inclusdébut, Key cléfin, boolean inclusfin) L'interface java.util.navigablemap<key, Value> qui hérite de java.util.sortedmap<key, Value> Fournit une vue formée de la partie de la carte formée des éléments dont la clé est supérieure (ou égale si inclusdébut est vrai) à clédébut et inférieure (ou égale si inclusfin est vrai) à cléfin. SortedMap<Key, Value> tailmap(key clédébut) Fournit une vue formée de la partie de la carte dont les éléments ont une clé supérieure à clédébut. NavigableMap<Key, Value> tailmap(key clédébut, boolean inclus) Fournit une vue de la partie de la carte dont les éléments ont une clé supérieure (ou égale si inclus est vrai) à clédébut. L'interface java.util.sortedmap<key, Value> Comparator<? super Key> comparator() Fournit le comparateur prévu à la construction, null sinon. Key firstkey() Fournit la première clé si elle existe, null sinon. SortedMap<Key, Value> headmap(key clémaxi) Fournit une vue des éléments de clé inférieure à clémaxi. Key lastkey() Fournit la dernière clé si elle existe, null sinon.

36 SortedMap<Key, Value> submap(key clémini, Key clémaxi) Fournit une vue des éléments de clé supérieure ou égale à clémini et inférieure à clémaxi. SortedMap<Key, Value> tailmap(key clémini) Fournit une vue des éléments de clé supérieure ou égale à clémini. Exemple d'application d'un répertoire téléphonique (un seul numéro par personne) A titre d'exemple, je vous propose de mettre en oeuvre un répertoire téléphonique dans l'ordre alphabétique. Pour cela j'utilise la classe TreeMap. Cette classe TreeMap est intéressante à plus d'un titre puisque je peux utiliser toutes ses compétences de navigations à l'aide des méthodes redéfinies issues de l'interface NavigableMap. La clé de la carte correspond au prénom de la personne alors que la valeur est le numéro de téléphone. Ainsi, l'ordre alphabétique intervient uniquement sur les clés. Dans les deux cas, le type d'élément est une chaîne de caractères. D'ailleurs, la saisie du numéro de téléphone passe par un masque afin que le signe moins apparaise automatiquement. package carte; import java.awt.*; import java.awt.event.actionevent; import java.text.parseexception; import javax.swing.*; import java.util.*; import javax.swing.text.maskformatter; public class Répertoire extends JFrame { private JTextField nom = new JTextField("Nouveau nom", 18); private JFormattedTextField téléphone; private Résultat liste = new Résultat(); private JPanel panneau = new JPanel(); private JToolBar barre = new JToolBar(); private JTextField nombre = new JTextField("0"); private TreeMap<String, String> répertoire = new TreeMap<String, String>(); private JComboBox déjàintroduit = new JComboBox(répertoire.entrySet().toArray()); public Répertoire() throws ParseException { super("répertoire téléphonique"); téléphone = new JFormattedTextField(new MaskFormatter("##-##-##-##-##")); téléphone.setvalue(" "); nombre.seteditable(false); nombre.sethorizontalalignment(jlabel.center); Action ajouter = new AbstractAction("Ajout") { répertoire.put(nom.gettext(),(string) téléphone.getvalue()); déjàintroduit.additem(répertoire.floorentry(nom.gettext())); analysecarte(); ; barre.add(ajouter); barre.add(new AbstractAction("Recherche") { téléphone.setvalue(répertoire.get(nom.gettext())); analysecarte(); ); barre.add(new AbstractAction("Suppression") { déjàintroduit.removeitem(répertoire.ceilingentry(nom.gettext())); répertoire.remove(nom.gettext()); analysecarte(); ); barre.add(new AbstractAction("Tout effacer") { répertoire.clear(); déjàintroduit.removeallitems(); analysecarte(); ); barre.addseparator(); barre.add(new JLabel("Nombre : ")); barre.add(nombre); add(barre, BorderLayout.NORTH); téléphone.addactionlistener(ajouter); panneau.add(new JLabel("Nom : ")); panneau.add(nom); panneau.add(new JLabel("Téléphone : ")); panneau.add(téléphone); panneau.add(new JLabel("Liste du répertoire : ")); panneau.add(déjàintroduit); add(panneau); add(liste, BorderLayout.SOUTH); setsize(470, 170);

37 setlocationrelativeto(null); setresizable(false); setdefaultcloseoperation(exit_on_close); setvisible(true); public void analysecarte() { liste.repaint(); nombre.settext(""+répertoire.size()); public static void main(string[] args) throws ParseException { new Répertoire(); private class Résultat extends JLabel { public Résultat() { setforeground(color.red); public void paint(graphics g) { settext(répertoire.tostring()); super.paint(g); Autre exemple d'un répertoire téléphonique (plusieurs numéros pour la même personne) Nous allons reprendre le répertoire téléphonique dans l'ordre alphabétique, mais cette fois-ci, il sera possible d'associer plusieurs numéros pour la même personne. J'utilise encore la classe TreeMap afin de préserver l'ordre alphabétique, avec la même clé que précédemment. Ce qui change, c'est la valeur qui devient elle même une carte de type énumération, implémentée donc par la classe EnumMap. Cette carte propose ainsi trois types de téléphone : Mobile, Domicile et Travail. Pour le numéro de téléphone, je conserve le même masque. package carte; import java.awt.*; import java.awt.event.actionevent; import java.text.parseexception; import javax.swing.*; import java.util.*; import javax.swing.text.maskformatter; public class Répertoire extends JFrame { private JTextField prénom = new JTextField("Nouveau prénom", 12); private JFormattedTextField téléphone; private JPanel panneau = new JPanel(); private JToolBar barre = new JToolBar(); private JComboBox liste = new JComboBox(); private enum TypeTéléphone {Mobile, Domicile, Travail; private TreeMap<String, EnumMap<TypeTéléphone, String>> répertoire = new TreeMap<String, EnumMap<TypeTéléphone, String>>(); private JComboBox type = new JComboBox(TypeTéléphone.values()); public Répertoire() throws ParseException { super("répertoire téléphonique"); téléphone = new JFormattedTextField(new MaskFormatter(" ##-##-##-##-## ")); téléphone.setvalue(" "); liste.setforeground(color.red); Action ajouter = new AbstractAction("Ajout") { EnumMap<TypeTéléphone, String> téléphones; if (répertoire.containskey(prénom.gettext())) { téléphones = répertoire.get(prénom.gettext()); téléphones.put((typetéléphone)type.getselecteditem(),(string)téléphone.getvalue()); else { téléphones = new EnumMap<TypeTéléphone, String>(TypeTéléphone.class); répertoire.put(prénom.gettext(), téléphones); téléphones.put((typetéléphone)type.getselecteditem(),(string)téléphone.getvalue()); liste.removeallitems(); for (Map.Entry<String, EnumMap<TypeTéléphone, String>> carte : répertoire.entryset()) liste.additem(carte); ; barre.add(ajouter); barre.add(new AbstractAction("Suppression") { liste.removeitem(répertoire.ceilingentry(prénom.gettext())); répertoire.remove(prénom.gettext()); ); barre.add(new AbstractAction("Tout effacer") {

38 répertoire.clear(); type.removeallitems(); ); barre.addseparator(); add(barre, BorderLayout.NORTH); téléphone.addactionlistener(ajouter); panneau.add(new JLabel("Prénom : ")); panneau.add(prénom); panneau.add(new JLabel("Téléphone : ")); panneau.add(type); panneau.add(téléphone); add(panneau); add(liste, BorderLayout.SOUTH); setsize(480, 130); setlocationrelativeto(null); setresizable(false); setdefaultcloseoperation(exit_on_close); setvisible(true); public static void main(string[] args) throws ParseException { new Répertoire(); La structure des collections Les classes sont regroupées dans une structure appelée cadre (ou framework), qui constitue la base commune pour créer des fonctionnalités avancées. Un cadre contient des superclasses renfermant des fonctionnalités pratiques, des méthodes et des mécanismes. L'utilisateur d'un cadre crée des sous-classes pour étendre les fonctionnalités sans devoir réinventer les mécanismes de base. Par exemple, Swing est un cadre pour les interfaces utilisateurs. La bibliothèque de collection Java forme un cadre pour les classes de collection. Ce cadre définit un certain nombre d'interfaces et de classes abstraites pour des implémentations de collections et il propose certains mécanismes, comme un protocole d'itération. Vous pouvez vous servir des classes de collection sans connaître grand-chose au cadre. C'est exactement ce que nous avons fait dans les chapitres précédents. En revanche, si vous souhaitez implémenter des algorithmes généraux qui fonctionnent sur plusieurs types de collection ou si vous voulez créer un nouveau type de collection, il vaut mieux comprendre le fonctionnement du cadre. Les hiérarchies des interfaces de type Collection et de type Map Il existe deux interfaces fondamentales pour les collections : Collection et Map. Suivant le type de collection, nous pouvons : 1. Insertion d'un élément : Des éléments peuvent être insérés dans une collection avec la méthode : boolean add(e élément). Les cartes renferment des paires clé/valeur, et la méthode put() sert à les ajouter dans une carte : Value put(key clé, Value valeur) 2. Lecture d'un élément : Pour lire les éléments dans une collection, il faut les parcourir avec un itérateur. Pour lire les valeurs d'une carte, il faut avoir recours à la méthode : Value get(key clé). 3. L'interface List : Une List est une collection triée. Les éléments sont ajoutés à une position particulière du conteneur. Un objet peut être inséré à la position adéquate de deux manières : par un indice entier ou par un itérateur de liste. L'interface List définit des méthodes pour accéder à n'importe qu'elle donnée d'une liste : void add(int index, E élément) E get(int index) void remove(int index) 4. Accès direct (aléatoire) : Comme nous l'avons déjà indiqué, l'interface List fournit ces méthodes d'accès aléatoires (directs) qu'elles soient ou non efficaces pour une implémentation particulière. Pour éviter de réaliser des opérations d'accès coûteuses, Java 1.4 a introduit une interface de balisage RandomAccess. Cette interface ne possède pas de méthodes, mais vous pouvez l'utiliser pour tester si une collection particulière prend en charge un accès direct efficace : if (c instanceof RandomAccess) { utiliser un algorithme d'accès direct (aléatoire)

39 else { utiliser un alogorithme d'accès séquentiel Les classes ArrayList et Vector implémentent l'interface RandomAccess. Les élément de ces deux classes sont justement contigües en mémoire et permettent d'avoir ainsi un accès direct aléatoire) efficace. 5. L'interface ListIterator : cette interface définit une méthode pour ajouter un élément juste avant la position de l'itérateur : void add(e élément). 6. L'interface Iterator : Pour lire et supprimer des éléments à une position particulière, il suffit d'utiliser les méthodes next() et remove() de l'interface Iterator. 7. L'interface Set : cette interface est identique à l'interface Collection, mais le comportement des méthodes y est défini de manière plus précise. La méthode add() d'un ensemble doit rejeter les valeurs doubles. La méthode equals() d'un ensemble doit être redéfinie de telle sorte que deux ensembles sont identiques s'ils possèdent les mêmes éléments, mais pas nécessairement disposé dans le même ordre. La méthode hashcode() doit être redéfinie pour que deux ensembles possédant les mêmes éléments soient associés au même code de hachage. Pourquoi définir une interface séparée si les signatures des méthodes sont les mêmes? Conceptuellement, toutes les collections ne sont pas des ensembles. La définition d'une interface Set permet aux programmeurs d'écrire des méthodes qui n'acceptent que des ensembles. 8. Les interfaces SortedSet et SortedMap : Ces interfaces mettent en évidence l'objet de comparaison utilisé pour triéer les éléments et elles définissent des méthodes pour obtenir des vues des sous-ensembles des collections. 9. Les interfaces NavigableSet et NavigableMap : Java SE 6 a introduit ces interfaces contenant des méthodes supplémentaires pour rechercher er parcourir des ensembles et des cartes triées (dans l'idéal, vous devez simplement avoir inclus ces méthodes dans les interfaces SortedSet et SortedMap). Les classes TreeSet et TreeMap implémentent ces interfaces. Les classes qui implémentent ces différentes interfaces Passons maintenant des interfaces aux classes qui les implémentent. Nous avons déjà vu que les interfaces de collections possèdent quelques méthodes qui peuvent être implémentées très simplement à partir d'un nombre plus important de méthodes fondamentales. Les classes abstraites fournissent certaines implémentations de ces routines : 1. AbstractCollection 2. AbstractList 3. AbstractSequenceList 4. AbstractSet 5. AbstractQueue 6. AbstractMap Si vous implémentez votre propre classe de collection, vous voudrez probablement étendre l'une de ces classes pour récupérer les implémentation de ces opérations. En tout cas, la bibliothèque Java fournit des classes concrètes associées : 1. LinkedList 2. ArrayList 3. ArrayDeque 4. HashSet 5. TreeSet 6. PriorityQueue 7. HashMap 8. TreeMap Les vues et les emballages Lorsque nous regardons les deux diagrammes de classes précédents, vous trouverez peut-être qu'il est exagéré d'avoir de nombreuses interfaces et classes abstraites pour implémenter un nombre modeste de collections concrètes. Mais ces diagrammes n'expliquent pas tout. En ayant recours à des vues, vous pouvez obtenir d'autres objets qui implémentent les interfaces Collection ou Map. Nous avons vu un exemple dans le chapitre précédent au moyen de la méthode keyset() des classes de cartes.

TP n 2 Concepts de la programmation Objets Master 1 mention IL, semestre 2 Le type Abstrait Pile

TP n 2 Concepts de la programmation Objets Master 1 mention IL, semestre 2 Le type Abstrait Pile TP n 2 Concepts de la programmation Objets Master 1 mention IL, semestre 2 Le type Abstrait Pile Dans ce TP, vous apprendrez à définir le type abstrait Pile, à le programmer en Java à l aide d une interface

Plus en détail

Pour signifier qu'une classe fille hérite d'une classe mère, on utilise le mot clé extends class fille extends mère

Pour signifier qu'une classe fille hérite d'une classe mère, on utilise le mot clé extends class fille extends mère L'héritage et le polymorphisme en Java Pour signifier qu'une classe fille hérite d'une classe mère, on utilise le mot clé extends class fille extends mère En java, toutes les classes sont dérivée de la

Plus en détail

Généralités sur le Langage Java et éléments syntaxiques.

Généralités sur le Langage Java et éléments syntaxiques. Généralités sur le Langage Java et éléments syntaxiques. Généralités sur le Langage Java et éléments syntaxiques....1 Introduction...1 Genéralité sur le langage Java....1 Syntaxe de base du Langage...

Plus en détail

Langage Java. Classe de première SI

Langage Java. Classe de première SI Langage Java Table des matières 1. Premiers pas...2 1.1. Introduction...2 1.2. Mon premier programme...2 1.3. Les commentaires...2 2. Les variables et les opérateurs...2 3. La classe Scanner...3 4. Les

Plus en détail

LMI 2. Programmation Orientée Objet POO - Cours 9. Said Jabbour. jabbour@cril.univ-artois.fr www.cril.univ-artois.fr/~jabbour

LMI 2. Programmation Orientée Objet POO - Cours 9. Said Jabbour. jabbour@cril.univ-artois.fr www.cril.univ-artois.fr/~jabbour LMI 2 Programmation Orientée Objet POO - Cours 9 Said Jabbour jabbour@cril.univ-artois.fr www.cril.univ-artois.fr/~jabbour CRIL UMR CNRS 8188 Faculté des Sciences - Univ. Artois Février 2011 Les collections

Plus en détail

4. Groupement d objets

4. Groupement d objets Conception objet en Java avec BlueJ une approche interactive 4. Groupement d objets Collections et itérateurs David J. Barnes, Michael Kölling version française: Patrice Moreaux Rédigé avec 1.0 Principaux

Plus en détail

Java Licence Professionnelle 2009-2010. Cours 7 : Classes et méthodes abstraites

Java Licence Professionnelle 2009-2010. Cours 7 : Classes et méthodes abstraites Java Licence Professionnelle 2009-2010 Cours 7 : Classes et méthodes abstraites 1 Java Classes et méthodes abstraites - Le mécanisme des classes abstraites permet de définir des comportements (méthodes)

Plus en détail

Utilisation d objets : String et ArrayList

Utilisation d objets : String et ArrayList Chapitre 6 Utilisation d objets : String et ArrayList Dans ce chapitre, nous allons aborder l utilisation d objets de deux classes prédéfinies de Java d usage très courant. La première, nous l utilisons

Plus en détail

Package Java.util Classe générique

Package Java.util Classe générique Package Java.util Classe générique 1 Classe Vector La taille est dynamique: dès qu un tableau vectoriel est plein, sa taille est doublée, triplée, etc. automatiquement Les cases sont de type Object add(object

Plus en détail

Chapitre 10. Les interfaces Comparable et Comparator 1

Chapitre 10. Les interfaces Comparable et Comparator 1 Chapitre 10: Les interfaces Comparable et Comparator 1/5 Chapitre 10 Les interfaces Comparable et Comparator 1 1 Ce chapitre a été extrait du document "Objets, Algorithmes, Patterns" de [René Lalement],

Plus en détail

Chapitre V. Les classes : Object, Vector, etc.

Chapitre V. Les classes : Object, Vector, etc. Chapitre V Les classes de base Les classes : Object, Vector, etc. Plan La classe Object Les collections La classe ArrayList La classe Vector Les classes Wrappers Les classes String et StringBuffer La classe

Plus en détail

TD3: tableaux avancées, première classe et chaînes

TD3: tableaux avancées, première classe et chaînes TD3: tableaux avancées, première classe et chaînes de caractères 1 Lestableaux 1.1 Élémentsthéoriques Déclaration des tableaux Pour la déclaration des tableaux, deux notations sont possibles. La première

Plus en détail

Encapsulation. L'encapsulation consiste à rendre les membres d'un objet plus ou moins visibles pour les autres objets.

Encapsulation. L'encapsulation consiste à rendre les membres d'un objet plus ou moins visibles pour les autres objets. Encapsulation L'encapsulation consiste à rendre les membres d'un objet plus ou moins visibles pour les autres objets. La visibilité dépend des membres : certains membres peuvent être visibles et d'autres

Plus en détail

Une introduction à Java

Une introduction à Java Une introduction à Java IFT 287 (Semaine 1) UNIVERSITÉ DE SHERBROOKE 1 Java - Historique Développé par Sun Microsystems en 1994 Inventeur James Gosling (canadien!) Objectif langage sûr (fortement typé)

Plus en détail

Recherche dans un tableau

Recherche dans un tableau Chapitre 3 Recherche dans un tableau 3.1 Introduction 3.1.1 Tranche On appelle tranche de tableau, la donnée d'un tableau t et de deux indices a et b. On note cette tranche t.(a..b). Exemple 3.1 : 3 6

Plus en détail

TP1 : Initiation à Java et Eclipse

TP1 : Initiation à Java et Eclipse TP1 : Initiation à Java et Eclipse 1 TP1 : Initiation à Java et Eclipse Systèmes d Exploitation Avancés I. Objectifs du TP Ce TP est une introduction au langage Java. Il vous permettra de comprendre les

Plus en détail

Premiers Pas en Programmation Objet : les Classes et les Objets

Premiers Pas en Programmation Objet : les Classes et les Objets Chapitre 2 Premiers Pas en Programmation Objet : les Classes et les Objets Dans la première partie de ce cours, nous avons appris à manipuler des objets de type simple : entiers, doubles, caractères, booléens.

Plus en détail

Programmation par les Objets en Java

Programmation par les Objets en Java Programmation par les Objets en Java Najib TOUNSI Les classes en Java (TD 3) I. Notion de classe I.1 Classe, champs, méthodes, instanciation, this, private vs. public. Créer une classe Point (coordonnée

Plus en détail

Java Licence Professionnelle CISII, 2009-2010. Cours 2 : Classes et Objets

Java Licence Professionnelle CISII, 2009-2010. Cours 2 : Classes et Objets Licence Professionnelle CISII, 2009-2010 Cours 2 : Classes et Objets 1 Classes et Objets Objectifs des LOO : - Manipuler des objets - Découper les programmes suivant les types des objets manipulés - Regrouper

Plus en détail

Introduction. Les méthodes. Les méthodes. Identité et égalité. Identité et égalité. La copie d'objets. Identité et égalité.

Introduction. Les méthodes. Les méthodes. Identité et égalité. Identité et égalité. La copie d'objets. Identité et égalité. Introduction La classe object Tous les objets Java héritent de la classe java.lang.object. Elle garantit que tout objet Java contient quelques méthodes fondamentales duplication comparaison Chaque classe

Plus en détail

Java Licence Professionnelle CISII, 2009-10

Java Licence Professionnelle CISII, 2009-10 Java Licence Professionnelle CISII, 2009-10 Cours 4 : Programmation structurée (c) http://www.loria.fr/~tabbone/cours.html 1 Principe - Les méthodes sont structurées en blocs par les structures de la programmation

Plus en détail

Programmation Objet - Cours II

Programmation Objet - Cours II Programmation Objet - Cours II - Exercices - Page 1 Programmation Objet - Cours II Exercices Auteur : E.Thirion - Dernière mise à jour : 05/07/2015 Les exercices suivants sont en majorité des projets à

Plus en détail

P r ob lé m a t iq u e d e la g é n é r icit é. Pr in cip e d e la g é n é r icit é e n Ja v a ( 1 /3 )

P r ob lé m a t iq u e d e la g é n é r icit é. Pr in cip e d e la g é n é r icit é e n Ja v a ( 1 /3 ) P r ob lé m a t iq u e d e la g é n é r icit é les versions de Java antérieures à 1.5 permettaient de créer des classes de structures contenant n'importe quels types d'objet : les collections (classes

Plus en détail

Programmer en JAVA. par Tama (tama@via.ecp.fr( tama@via.ecp.fr)

Programmer en JAVA. par Tama (tama@via.ecp.fr( tama@via.ecp.fr) Programmer en JAVA par Tama (tama@via.ecp.fr( tama@via.ecp.fr) Plan 1. Présentation de Java 2. Les bases du langage 3. Concepts avancés 4. Documentation 5. Index des mots-clés 6. Les erreurs fréquentes

Plus en détail

TP, première séquence d exercices.

TP, première séquence d exercices. TP, première séquence d exercices. Benoît Valiron benoit.valiron@lipn.univ-paris13.fr 7 novembre 2010 Introduction Vous écrirez les réponses aux questions courtes sur une feuille à rendre à la fin de la

Plus en détail

Cours 1: Java et les objets

Cours 1: Java et les objets Ressources Les interface homme-machine et le langage Java DUT première année Henri Garreta, Faculté des Sciences (Luminy) Cyril Pain-Barre & Sébastien Nedjar, IUT d Aix-Marseille (Aix) Cours 1: infodoc.iut.univ-aix.fr/~ihm/

Plus en détail

Héritage presque multiple en Java (1/2)

Héritage presque multiple en Java (1/2) Héritage presque multiple en Java (1/2) Utiliser deux classes ou plus dans la définition d'une nouvelle classe peut se faire par composition. class Etudiant{ int numero; Diplome d; float passeexamen(examen

Plus en détail

Info0101 Intro. à l'algorithmique et à la programmation. Cours 3. Le langage Java

Info0101 Intro. à l'algorithmique et à la programmation. Cours 3. Le langage Java Info0101 Intro. à l'algorithmique et à la programmation Cours 3 Le langage Java Pierre Delisle, Cyril Rabat et Christophe Jaillet Université de Reims Champagne-Ardenne Département de Mathématiques et Informatique

Plus en détail

RAPPELS SUR LES METHODES HERITEES DE LA CLASSE RACINE Object ET LEUR SPECIALISATION (i.e. REDEFINITION)

RAPPELS SUR LES METHODES HERITEES DE LA CLASSE RACINE Object ET LEUR SPECIALISATION (i.e. REDEFINITION) CLASSE RACINE Object ancêtre de toutes les classes RAPPELS SUR LES METHODES HERITEES DE LA CLASSE RACINE Object ET LEUR SPECIALISATION (i.e. REDEFINITION) définit donc des méthodes héritées par toutes

Plus en détail

Auto-évaluation Programmation en Java

Auto-évaluation Programmation en Java Auto-évaluation Programmation en Java Document: f0883test.fm 22/01/2013 ABIS Training & Consulting P.O. Box 220 B-3000 Leuven Belgium TRAINING & CONSULTING INTRODUCTION AUTO-ÉVALUATION PROGRAMMATION EN

Plus en détail

INTRODUCTION A JAVA. Fichier en langage machine Exécutable

INTRODUCTION A JAVA. Fichier en langage machine Exécutable INTRODUCTION A JAVA JAVA est un langage orienté-objet pur. Il ressemble beaucoup à C++ au niveau de la syntaxe. En revanche, ces deux langages sont très différents dans leur structure (organisation du

Plus en détail

Plan du cours. Historique du langage http://www.oracle.com/technetwork/java/index.html. Nouveautés de Java 7

Plan du cours. Historique du langage http://www.oracle.com/technetwork/java/index.html. Nouveautés de Java 7 Université Lumière Lyon 2 Faculté de Sciences Economiques et Gestion KHARKIV National University of Economic Introduction au Langage Java Master Informatique 1 ère année Julien Velcin http://mediamining.univ-lyon2.fr/velcin

Plus en détail

Un ordonnanceur stupide

Un ordonnanceur stupide Un ordonnanceur simple Université Paris Sud L objet des exercices qui suivent est de créer un ordonanceur implantant l algorithme du tourniquet ( round-robin scheduler ). La technique utilisée pour élire

Plus en détail

Solutions du chapitre 4

Solutions du chapitre 4 Solutions du chapitre 4 Structures de contrôle: première partie 4.9 Identifiez et corrigez les erreurs (il peut y en avoir plus d une par segment de code) de chacune des proposition suivantes: a) if (

Plus en détail

SHERLOCK 7. Version 1.2.0 du 01/09/09 JAVASCRIPT 1.5

SHERLOCK 7. Version 1.2.0 du 01/09/09 JAVASCRIPT 1.5 SHERLOCK 7 Version 1.2.0 du 01/09/09 JAVASCRIPT 1.5 Cette note montre comment intégrer un script Java dans une investigation Sherlock et les différents aspects de Java script. S T E M M E R I M A G I N

Plus en détail

Lambda! Rémi Forax Univ Paris-Est Marne-la-Vallée

Lambda! Rémi Forax Univ Paris-Est Marne-la-Vallée Lambda! Rémi Forax Univ Paris-Est Marne-la-Vallée forax at univ-mlv dot fr - ParisJUG Java.next() - Mars 2012 What Else? Lambda == Inner class? Java a des lambdas depuis la version 1.1 Exemple utilisant

Plus en détail

Chapitre 2. Classes et objets

Chapitre 2. Classes et objets Chapitre 2: Classes et Objets 1/10 Chapitre 2 Classes et objets Chapitre 2: Classes et Objets 2/10 Approche Orientée Objet Idée de base de A.O.O. repose sur l'observation de la façon dont nous procédons

Plus en détail

TD/TP PAC - Programmation n 3

TD/TP PAC - Programmation n 3 Université Paris Sud Licence d informatique/iup-miage2 - Année 2004-2005 auteur : Frédéric Vernier semaine : 11-16 octobre 2004 conditions : sur machine avec les outils standards java web: http://vernier.frederic.free.fr/indexpac.html

Plus en détail

Java 7 Les fondamentaux du langage Java

Java 7 Les fondamentaux du langage Java 184 Java 7 Les fondamentaux du langage Java 1.1 Les bibliothèques graphiques Le langage Java propose deux bibliothèques dédiées à la conception d'interfaces graphiques. La bibliothèque AWT et la bibliothèque

Plus en détail

Licence Bio Informatique Année 2004-2005. Premiers pas. Exercice 1 Hello World parce qu il faut bien commencer par quelque chose...

Licence Bio Informatique Année 2004-2005. Premiers pas. Exercice 1 Hello World parce qu il faut bien commencer par quelque chose... Université Paris 7 Programmation Objet Licence Bio Informatique Année 2004-2005 TD n 1 - Correction Premiers pas Exercice 1 Hello World parce qu il faut bien commencer par quelque chose... 1. Enregistrez

Plus en détail

Généricité. en Java. (polymorphisme paramétrique) Philippe GENOUD UJF Janvier 2015 1

Généricité. en Java. (polymorphisme paramétrique) Philippe GENOUD UJF Janvier 2015 1 (polymorphisme paramétrique) en Java Philippe GENOUD UJF Janvier 2015 1 Motivations Supposons que l'on développe du code pour gérer une file d'attente (FIFO First In First Out) et que l'on veuille utiliser

Plus en détail

as Architecture des Systèmes d Information

as Architecture des Systèmes d Information Plan Plan Programmation - Introduction - Nicolas Malandain March 14, 2005 Introduction à Java 1 Introduction Présentation Caractéristiques Le langage Java 2 Types et Variables Types simples Types complexes

Plus en détail

Tp 1 correction. Structures de données (IF2)

Tp 1 correction. Structures de données (IF2) Tp 1 correction Structures de données (IF2) Remarque générale : compilez et exécutez le code au-fur-et-à mesure de son écriture. Il est plus facile de corriger une petite portion de code délimitée que

Plus en détail

I. Introduction aux fonctions : les fonctions standards

I. Introduction aux fonctions : les fonctions standards Chapitre 3 : Les fonctions en C++ I. Introduction aux fonctions : les fonctions standards A. Notion de Fonction Imaginons que dans un programme, vous ayez besoin de calculer une racine carrée. Rappelons

Plus en détail

Compte-rendu de projet de Système de gestion de base de données

Compte-rendu de projet de Système de gestion de base de données Compte-rendu de projet de Système de gestion de base de données Création et utilisation d'un index de jointure LAMBERT VELLER Sylvain M1 STIC Université de Bourgogne 2010-2011 Reponsable : Mr Thierry Grison

Plus en détail

Facultés Universitaires Notre-Dame de la Paix. Conception et Programmation Orientées- Object

Facultés Universitaires Notre-Dame de la Paix. Conception et Programmation Orientées- Object Facultés Universitaires Notre-Dame de la Paix Conception et Programmation Orientées- Object 2008-2009 RÉSUMÉ PRATIQUE... 4 CLASSE OBJET... 4 NOTION D HÉRITAGE... 4 LE POLYMORPHISME... 5 LES CLASSES ABSTRAITES...

Plus en détail

Prénom : Matricule : Sigle et titre du cours Groupe Trimestre INF1101 Algorithmes et structures de données Tous H2004. Loc Jeudi 29/4/2004

Prénom : Matricule : Sigle et titre du cours Groupe Trimestre INF1101 Algorithmes et structures de données Tous H2004. Loc Jeudi 29/4/2004 Questionnaire d'examen final INF1101 Sigle du cours Nom : Signature : Prénom : Matricule : Sigle et titre du cours Groupe Trimestre INF1101 Algorithmes et structures de données Tous H2004 Professeur(s)

Plus en détail

JAVA 8. JAVA 8 - Les fondamentaux du langage. Les fondamentaux du langage Java. Avec exercices pratiques et corrigés JAVA 8 29,90.

JAVA 8. JAVA 8 - Les fondamentaux du langage. Les fondamentaux du langage Java. Avec exercices pratiques et corrigés JAVA 8 29,90. Analyste et développeur pendant plus de 10 ans, Thierry GROUSSARD s est ensuite orienté vers la formation et plus particulièrement dans le domaine du développement. Sa connaissance approfondie des besoins

Plus en détail

Centre CPGE TSI - Safi 2010/2011. Algorithmique et programmation :

Centre CPGE TSI - Safi 2010/2011. Algorithmique et programmation : Algorithmique et programmation : STRUCTURES DE DONNÉES A. Structure et enregistrement 1) Définition et rôle des structures de données en programmation 1.1) Définition : En informatique, une structure de

Plus en détail

Cours 1 : Introduction. Langages objets. but du module. contrôle des connaissances. Pourquoi Java? présentation du module. Présentation de Java

Cours 1 : Introduction. Langages objets. but du module. contrôle des connaissances. Pourquoi Java? présentation du module. Présentation de Java Langages objets Introduction M2 Pro CCI, Informatique Emmanuel Waller, LRI, Orsay présentation du module logistique 12 blocs de 4h + 1 bloc 2h = 50h 1h15 cours, 45mn exercices table, 2h TD machine page

Plus en détail

Corrigé des exercices sur les références

Corrigé des exercices sur les références Corrigé des exercices sur les références Exercice 3.1.1 dessin Pour cet exercice, vous allez dessiner des structures de données au moyen de petits schémas analogues à ceux du cours, comportant la pile

Plus en détail

Apprendre Java en 154 minutes

Apprendre Java en 154 minutes Apprendre Java en 154 minutes Tutorial Java 1.6 - HESB-TI Stéphanie Calderara José R. Beuret Quentin Cosendey Mai 2008 Table des matières 1 Objet, classe et modificateur 3 1.1 Le premier pas..........................

Plus en détail

Polymorphisme, la classe Object, les package et la visibilité en Java... 1

Polymorphisme, la classe Object, les package et la visibilité en Java... 1 Polymorphisme, la classe Object, les package et la visibilité en Java. Polymorphisme, la classe Object, les package et la visibilité en Java.... 1 Polymorphisme.... 1 Le DownCast... 4 La Classe Object....

Plus en détail

Introduction à JDBC. Accès aux bases de données en Java

Introduction à JDBC. Accès aux bases de données en Java Introduction à JDBC Accès aux bases de données en Java Eric Cariou Université de Pau et des Pays de l'adour Département Informatique Eric.Cariou@univ-pau.fr 1 Introduction JDBC : Java Data Base Connectivity

Plus en détail

Initiation à JAVA et à la programmation objet. raphael.bolze@ens-lyon.fr

Initiation à JAVA et à la programmation objet. raphael.bolze@ens-lyon.fr Initiation à JAVA et à la programmation objet raphael.bolze@ens-lyon.fr O b j e c t i f s Découvrir un langage de programmation objet. Découvrir l'environnement java Découvrir les concepts de la programmation

Plus en détail

ACTIVITÉ DE PROGRAMMATION

ACTIVITÉ DE PROGRAMMATION ACTIVITÉ DE PROGRAMMATION The purpose of the Implementation Process is to realize a specified system element. ISO/IEC 12207 Sébastien Adam Une introduction 2 Introduction Ø Contenu Utilité de l ordinateur,

Plus en détail

Programmation Par Objets

Programmation Par Objets Programmation Par Objets Structures de données package java.util B. Carré Polytech Lille 1 Tableaux et structures de données Tableaux «Objets» taille fixe type des éléments : primitif (homogène) ou objets

Plus en détail

1. Introduction...2. 2. Création d'une requête...2

1. Introduction...2. 2. Création d'une requête...2 1. Introduction...2 2. Création d'une requête...2 3. Définition des critères de sélection...5 3.1 Opérateurs...5 3.2 Les Fonctions...6 3.3 Plusieurs critères portant sur des champs différents...7 3.4 Requête

Plus en détail

TD/TP PAC - Programmation n 3

TD/TP PAC - Programmation n 3 Université Paris Sud Licence d informatique/iup-miage2 Année 2004-2005 Auteur : Frédéric Vernier Semaine : 11-16 octobre 2004 Conditions : sur machine avec les outils standards java web: http://vernier.frederic.free.fr/indexpac.html

Plus en détail

Cours intensif Java. 1er cours: de C à Java. Enrica DUCHI LIAFA, Paris 7. Septembre 2009. Enrica.Duchi@liafa.jussieu.fr

Cours intensif Java. 1er cours: de C à Java. Enrica DUCHI LIAFA, Paris 7. Septembre 2009. Enrica.Duchi@liafa.jussieu.fr . Cours intensif Java 1er cours: de C à Java Septembre 2009 Enrica DUCHI LIAFA, Paris 7 Enrica.Duchi@liafa.jussieu.fr LANGAGES DE PROGRAMMATION Pour exécuter un algorithme sur un ordinateur il faut le

Plus en détail

Langage et Concepts de ProgrammationOrientée-Objet 1 / 40

Langage et Concepts de ProgrammationOrientée-Objet 1 / 40 Déroulement du cours Introduction Concepts Java Remarques Langage et Concepts de Programmation Orientée-Objet Gauthier Picard École Nationale Supérieure des Mines de Saint-Étienne gauthier.picard@emse.fr

Plus en détail

Chapitre 1 : Introduction aux bases de données

Chapitre 1 : Introduction aux bases de données Chapitre 1 : Introduction aux bases de données Les Bases de Données occupent aujourd'hui une place de plus en plus importante dans les systèmes informatiques. Les Systèmes de Gestion de Bases de Données

Plus en détail

les Formulaires / Sous-Formulaires Présentation...2 1. Créer un formulaire à partir d une table...3

les Formulaires / Sous-Formulaires Présentation...2 1. Créer un formulaire à partir d une table...3 Présentation...2 1. Créer un formulaire à partir d une table...3 2. Les contrôles :...10 2.1 Le contrôle "Intitulé"...11 2.2 Le contrôle "Zone de Texte"...12 2.3 Le contrôle «Groupe d options»...14 2.4

Plus en détail

Chapitre VI- La validation de la composition.

Chapitre VI- La validation de la composition. Chapitre VI- La validation de la composition. Objectifs du chapitre : Expliquer les conséquences de l utilisation de règles de typage souples dans SEP. Présenter le mécanisme de validation des connexions

Plus en détail

Exceptions. 1 Entrées/sorties. Objectif. Manipuler les exceptions ;

Exceptions. 1 Entrées/sorties. Objectif. Manipuler les exceptions ; CNAM NFP121 TP 10 19/11/2013 (Séance 5) Objectif Manipuler les exceptions ; 1 Entrées/sorties Exercice 1 : Lire un entier à partir du clavier Ajouter une méthode readint(string message) dans la classe

Plus en détail

Programmation avec des objets : Cours 7. Menu du jour

Programmation avec des objets : Cours 7. Menu du jour 1 Programmation avec des objets : Cours 7 Menu du jour 1. Retour sur la classe Liste 2. Précisions sur l interface 3. Difficultés dans le cas d erreurs 4. Soulever des exceptions 5. Utilisation des Listes

Plus en détail

Exercices sur les interfaces

Exercices sur les interfaces Exercices sur les interfaces Fabrice Rossi 18 octobre 1999 1 Le type Object 1.1 Manipulations élémentaires Exercice 1.1 : Indiquer l affichage produit par le programme suivant : public class UpCast1 {

Plus en détail

Programmation Orientée Objet en C#

Programmation Orientée Objet en C# Programmation Orientée Objet en C# 1 Introduction 1.1 Présentation Tout bon développeur le sait, le code d'un programme doit être propre, commenté, facile à maintenir et à améliorer. Vous êtes adepte de

Plus en détail

Quelques patterns pour la persistance des objets avec DAO DAO. Principe de base. Utilité des DTOs. Le modèle de conception DTO (Data Transfer Object)

Quelques patterns pour la persistance des objets avec DAO DAO. Principe de base. Utilité des DTOs. Le modèle de conception DTO (Data Transfer Object) Quelques patterns pour la persistance des objets avec DAO Ce cours présente des modèles de conception utilisés pour effectuer la persistance des objets Université de Nice Sophia-Antipolis Version 1.4 30/8/07

Plus en détail

Conventions d écriture et outils de mise au point

Conventions d écriture et outils de mise au point Logiciel de base Première année par alternance Responsable : Christophe Rippert Christophe.Rippert@Grenoble-INP.fr Introduction Conventions d écriture et outils de mise au point On va utiliser dans cette

Plus en détail

UEO11 COURS/TD 1. nombres entiers et réels codés en mémoire centrale. Caractères alphabétiques et caractères spéciaux.

UEO11 COURS/TD 1. nombres entiers et réels codés en mémoire centrale. Caractères alphabétiques et caractères spéciaux. UEO11 COURS/TD 1 Contenu du semestre Cours et TDs sont intégrés L objectif de ce cours équivalent a 6h de cours, 10h de TD et 8h de TP est le suivant : - initiation à l algorithmique - notions de bases

Plus en détail

1. Introduction... 2. 2. Création d'une macro autonome... 2. 3. Exécuter la macro pas à pas... 5. 4. Modifier une macro... 5

1. Introduction... 2. 2. Création d'une macro autonome... 2. 3. Exécuter la macro pas à pas... 5. 4. Modifier une macro... 5 1. Introduction... 2 2. Création d'une macro autonome... 2 3. Exécuter la macro pas à pas... 5 4. Modifier une macro... 5 5. Création d'une macro associée à un formulaire... 6 6. Exécuter des actions en

Plus en détail

Travaux pratiques. Compression en codage de Huffman. 1.3. Organisation d un projet de programmation

Travaux pratiques. Compression en codage de Huffman. 1.3. Organisation d un projet de programmation Université de Savoie Module ETRS711 Travaux pratiques Compression en codage de Huffman 1. Organisation du projet 1.1. Objectifs Le but de ce projet est d'écrire un programme permettant de compresser des

Plus en détail

RMI le langage Java XII-1 JMF

RMI le langage Java XII-1 JMF Remote Method Invocation (RMI) XII-1 Introduction RMI est un ensemble de classes permettant de manipuler des objets sur des machines distantes (objets distants) de manière similaire aux objets sur la machine

Plus en détail

Java DataBaseConnectivity

Java DataBaseConnectivity Java DataBaseConnectivity JDBC JDBC est une API Java (ensemble de classes et d interfaces défini par SUN et les acteurs du domaine des SGBD) permettant d accéder aux bases de données à l aide du langage

Plus en détail

Programmation Orientée Objet Java

Programmation Orientée Objet Java Programmation Orientée Objet Java Bertrand Estellon Département Informatique et Interactions Aix-Marseille Université 12 novembre 2014 Bertrand Estellon (DII AMU) Programmation Orientée Objet Java 12 novembre

Plus en détail

C++ Programmer. en langage. 8 e édition. Avec une intro aux design patterns et une annexe sur la norme C++11. Claude Delannoy

C++ Programmer. en langage. 8 e édition. Avec une intro aux design patterns et une annexe sur la norme C++11. Claude Delannoy Claude Delannoy Programmer en langage C++ 8 e édition Avec une intro aux design patterns et une annexe sur la norme C++11 Groupe Eyrolles, 1993-2011. Groupe Eyrolles, 2014, pour la nouvelle présentation,

Plus en détail

Page 1 sur 5 TP3. Thèmes du TP : l la classe Object. l Vector<T> l tutorial Interfaces. l Stack<T>

Page 1 sur 5 TP3. Thèmes du TP : l la classe Object. l Vector<T> l tutorial Interfaces. l Stack<T> Page 1 sur 5 TP3 Lectures préalables : l Java_II l tutorial Interfaces Thèmes du TP : l la classe Object l Vector l Stack Une pile d'objects Les éléments de la classe Pile sont maintenant des instances

Plus en détail

Programme Compte bancaire (code)

Programme Compte bancaire (code) using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; Programme Compte bancaire (code) namespace compte_bancaire /// Classe Program public

Plus en détail

Gestion distribuée (par sockets) de banque en Java

Gestion distribuée (par sockets) de banque en Java Gestion distribuée (par sockets) de banque en Java Université Paris Sud Rappel sur la solution locale de gestion simple de banque L objet de cet exercice était de créer une application java qui implante

Plus en détail

Application web de gestion de comptes en banques

Application web de gestion de comptes en banques Application web de gestion de comptes en banques Objectif Réaliser une application Web permettant à un client de gérer ses comptes en banque Diagramme de cas d'utilisation 1 Les cas d'utilisation Connexion

Plus en détail

Java Licence professionnelle CISII, 2009-2010

Java Licence professionnelle CISII, 2009-2010 Java Licence professionnelle CISII, 2009-2010 Cours 6 : le paquetage (package) Cours inspiré des cours de Richard Grin, Antoine Tabbone et Hazel Everett 1 Définition - Les classes Java sont regroupées

Plus en détail

Le langage C. Séance n 4

Le langage C. Séance n 4 Université Paris-Sud 11 Institut de Formation des Ingénieurs Remise à niveau INFORMATIQUE Année 2007-2008 Travaux pratiques d informatique Le langage C Séance n 4 But : Vous devez maîtriser à la fin de

Plus en détail

Corrigés des premiers exercices sur les classes

Corrigés des premiers exercices sur les classes Corrigés des premiers exercices sur les classes Exercice 2.1.1 utilisation d une classe Voici le texte d une classe représentant de façon sommaire un compte bancaire et les opérations bancaires courantes.

Plus en détail

Objets et Programmation. origine des langages orientés-objet

Objets et Programmation. origine des langages orientés-objet Objets et Programmation origine des langages orientés-objet modularité, encapsulation objets, classes, messages exemples en Java héritage, liaison dynamique G. Falquet, Th. Estier CUI Université de Genève

Plus en détail

Structurer ses données : les tableaux. Introduction à la programmation

Structurer ses données : les tableaux. Introduction à la programmation Structurer ses données : les tableaux Introduction à la programmation Plan du document Introduction Introduire la notion de type Types primitifs Types composés Tableaux de type primitif Page 2 Notion de

Plus en détail

Introduction à MATLAB R

Introduction à MATLAB R Introduction à MATLAB R Romain Tavenard 10 septembre 2009 MATLAB R est un environnement de calcul numérique propriétaire orienté vers le calcul matriciel. Il se compose d un langage de programmation, d

Plus en détail

INF2015 Développement de logiciels dans un environnement Agile. Examen intra 20 février 2014 17:30 à 20:30

INF2015 Développement de logiciels dans un environnement Agile. Examen intra 20 février 2014 17:30 à 20:30 Examen intra 20 février 2014 17:30 à 20:30 Nom, prénom : Code permanent : Répondez directement sur le questionnaire. Question #1 5% Quelle influence peut avoir le typage dynamique sur la maintenabilité

Plus en détail

Création d'un questionnaire (sondage)

Création d'un questionnaire (sondage) Création d'un questionnaire (sondage) Le but de ce petit tuto est d'avoir les séquences pas à pas pour la création d'un questionnaire de façon à ne pas devoir rechercher la manière de procéder si l'outil

Plus en détail

Diagramme de classes

Diagramme de classes Diagramme de classes Un diagramme de classes décrit les classes et leurs relations (associations, généralisation/spécialisation, ). classe association méthodes attributs héritage Diagramme de classes :

Plus en détail

Algorithmique et Programmation, IMA

Algorithmique et Programmation, IMA Algorithmique et Programmation, IMA Cours 2 : C Premier Niveau / Algorithmique Université Lille 1 - Polytech Lille Notations, identificateurs Variables et Types de base Expressions Constantes Instructions

Plus en détail

Les chaînes de caractères

Les chaînes de caractères Les chaînes de caractères Dans un programme informatique, les chaînes de caractères servent à stocker les informations non numériques comme par exemple une liste de nom de personne ou des adresses. Il

Plus en détail

TD Objets distribués n 3 : Windows XP et Visual Studio.NET. Introduction à.net Remoting

TD Objets distribués n 3 : Windows XP et Visual Studio.NET. Introduction à.net Remoting IUT Bordeaux 1 2005-2006 Département Informatique Licence Professionnelle ~ SI TD Objets distribués n 3 : Windows XP et Visual Studio.NET Introduction à.net Remoting Partie 1 : l'analyseur de performances

Plus en détail

Interfaces graphiques avec l API Swing

Interfaces graphiques avec l API Swing Interfaces graphiques avec l API Swing Les Swing Les classes graphiques Swing dérivent de la classe JComponent, qui hérite ellemême de la classe AWT (Abstract Window Toolkit). Tous les composants Swing

Plus en détail

2 Grad Info Soir Langage C++ Juin 2007. Projet BANQUE

2 Grad Info Soir Langage C++ Juin 2007. Projet BANQUE 2 Grad Info Soir Langage C++ Juin 2007 Projet BANQUE 1. Explications L'examen comprend un projet à réaliser à domicile et à documenter : - structure des données, - objets utilisés, - relations de dépendance

Plus en détail

Java 1.5 : principales nouveautés

Java 1.5 : principales nouveautés Cours 6 - TEP - UPMC - 2008/2009 p. 1/34 Java 1.5 : principales nouveautés classes paramétrées : generics encapsulation des valeurs de types primitifs : auto[un]boxing itération sur les boucles types énumérés

Plus en détail

Introduction à Java. Matthieu Herrb CNRS-LAAS. Mars 2014. http://homepages.laas.fr/matthieu/cours/java/java.pdf

Introduction à Java. Matthieu Herrb CNRS-LAAS. Mars 2014. http://homepages.laas.fr/matthieu/cours/java/java.pdf Introduction à Java Matthieu Herrb CNRS-LAAS http://homepages.laas.fr/matthieu/cours/java/java.pdf Mars 2014 Plan 1 Concepts 2 Éléments du langage 3 Classes et objets 4 Packages 2/28 Histoire et motivations

Plus en détail

Programmation Orientée Objet

Programmation Orientée Objet Programmation Orientée Objet Bertrand Estellon Département Informatique et Interactions Aix-Marseille Université 21 octobre 2014 Bertrand Estellon (DII AMU) Programmation Orientée Objet 21 octobre 2014

Plus en détail

Threads. Threads. USTL http://www.lifl.fr/ routier 1

Threads. Threads. USTL http://www.lifl.fr/ routier 1 Threads USTL http://www.lifl.fr/ routier 1 Rappels - Définitions un Process est un programme qui tourne et dispose de ses propres ressources mémoire. OS multi-tâche plusieurs process en concurrence un

Plus en détail