3. STRUCTURES DE DONNEES
 
 
TABLEAUX
 

Un tableau en Java est un objet, donc comme toute variable objet, une variable tableau est une référence. Lors de sa déclaration, il n'est pas possible d'indiquer la taille du tableau.

int t[];

La taille n'est fournie qu'au moment de l'allocation.

t=new int[10];

Pour connaître la taille d'un tableau, il suffit de consulter l'attribut length de celui-ci.

int taille = t.length;

Il est possible d'allouer des tableaux à plusieurs dimensions, comme le montre l'exemple suivant qui construit une matrice 3x4 de chaînes de caractères.

String m[][] = new String[3][4];
int i = 0;
int j;

while (i<3) {
 j=0;

 while (j<4) {
  m[i][j]=new String("case "+i+";"+j);
  ++j;
 }

 ++i;
}

Attention, lors de l'allocation d'un tableau d'éléments de type primitif, les cases du tableau contiennent ces éléments. Lors de l'allocation d'un tableau d'objets d'une classe quelconque, les cases du tableau contiennent des références d'objets initialisées à null.

Un tableau à plusieurs dimensions est en fait un tableau de tableaux. Et il n'est pas obligatoire que ces derniers soient tous de même taille.

int m[][] = new int[3][];
int i = 0;

while (i<3) {
 m[i]=new int[5*(i+1)];
 j=0;

 while (j<5*(i+1)) {
  m[i][j]=i+j;
  ++j;
 }

 ++i;
}

Il est possible, comme en C++, d'initialiser directement un tableau à sa déclaration.

int a[] = {1,2,3};

 
CHAINE DE CARACTERES
 

Contrairement au C++, les chaînes de caractères en Java ne sont pas des tableaux de caractères. Le seul moyen d'obtenir une chaîne de caractères est d'utiliser la classe String.

String r = new String("bonjour");
String t = "bonjour";

Pour connaître la taille d'une chaîne de caractères, il suffit d'appeler la méthode length.

int taille = t.length();

Il est possible de concaténer deux chaînes de caractères grâce à l'opérateur +.

String s = t+" toi";

Un objet de la classe String n'étant pas un tableau de caractères, il n'est pas possible d'utiliser l'opérateur [] pour accéder à un caractère de la chaîne. Pour cela, il faut utiliser la méthode charAt.

char j = s.charAt(3);

L'opérateur == appliqué directement sur deux variables compare leurs références et non pas les objets qu'elles pointent. Pour comparer réellement deux objets, il faut utiliser leur méthode equals, héritée de la classe Object, et normalement redéfinie dans leur classe véritable.

if (s.equals("bonjour")) ...

La méthode indexOf permet de déterminer la position d'une chaîne de caractères dans une autre. Si aucune sous-chaîne n'est trouvée, alors la méthode renvoie -1.

int position = s.indexOf("toi");

Enfin, la méthode substring permet d'extraire une partie d'une chaîne de caractères, en indiquant la position du premier caractère et la position après le dernier caractère de la sous-chaîne.

String toi = s.substring(position,position+3);

 
TYPES PRIMITIFS
 

Voici la liste des types primitifs reconnus par Java. La plupart sont identiques à ceux du C++. La différence importante est que la norme Java impose un codage standard des types.

Type Description
byte entier signé, 8 bits
boolean booléen, 1 bit, true / false
char caractère, 16 bits, norme Unicode
short entier signé, 16 bits
int entier signé, 32 bits
long entier signé, 64 bits
float flottant, 32 bits, norme IEEE 754
double flottant, 64 bits, norme IEEE 754

Les variables de type primitif ne sont pas reconnues comme des objets, ce qui est très gênant lorsqu'on ne dispose pas des patrons de composant. Comment proposer par exemple une liste chaînée qui puisse à la fois contenir des entiers de type int et des objets de type String ? La solution est de transformer les variables de type primitif en objets. Comme toutes les classes en Java héritent de la classe Object, il est alors possible de proposer une liste chaînée d'objets de type Object. Tous les types primitifs ont leur équivalent en objet.

Type primitif Classe équivalente
byte Byte
boolean Boolean
char Character
short Short
int Integer
long Long
float Float
double Double

Nous présentons quelques fonctionnalités de la classe Integer, celles des autres classes sont très similaires. Tout d'abord, il est possible de créer à partir d'un entier un objet Integer.

Integer i = new Integer(4);

D'une chaîne de caractères, il est possible d'extraire un entier grâce à la méthode parseInt de la classe Integer. A noter que l'accès aux propriétés de classes en Java se fait en appliquant l'opérateur . sur la classe, alors qu'en C++ il s'agit de l'opérateur ::.

int i = Integer.parseInt("4");

La méthode lève une exception (cf. le dernier chapitre) si la conversion de la chaîne en entier est impossible. Naturellement, d'un objet Integer, il est possible de récupérer sa valeur sous la forme d'un entier.

int j = i.intValue();

 
CONTENEURS
 

A l'instar de C++ avec la STL (Standard Template Library), Java fournit en standard un certain nombre de conteneurs très utiles pour accélérer le développement des applications. Cependant, comme la notion de patron n'existe pas encore en Java, les conteneurs sont conçus de manière à stocker des objets de la classe Object, à laquelle tout objet appartient. Mais nous verrons dans cette section que cela impose au programmeur d'employer le downcast, mécanisme lent puisqu'il doit détecter en temps réel la classe de l'objet. En outre, pour les types primitifs, les conteneurs ne sont pas utilisables directement, il faut transformer les variables en des objets. En résumé, ces classes conteneurs sont très utiles, mais ne sont pas d'une très grande efficacité. Nous ne citerons ici que deux conteneurs, le vecteur et le dictionnaire, qui sont assez représentatifs. Nous aborderons également l'énumération, qui permet un parcours séquentiel des éléments d'un conteneur. Notons que tous ces éléments font partie du package java.util.

 
Vecteur
 

Le vecteur, la classe Vector, équivalente à la classe vector de la STL, représente un tableau redimensionnable, autrement dit quand on lui ajoute des éléments, il adapte sa taille et réalloue de la mémoire si nécessaire. Voici un exemple simple qui ajoute dix entiers dans un vecteur.

Vector v = new Vector();
int i = 0;

while (i<10) {
 v.addElement(new Integer(i));
 ++i;
}

Le tableau suivant propose une liste non exhaustive de méthodes qui permettent de manipuler un vecteur.

Méthode Description
v.lastElement() Retourne le dernier élément de v.
v.elementAt(i) Retourne l'élément de v à la position i.
v.setElementAt(o,i) Affecte o à l'élément de v à la position i.
v.insertElementAt(o,i) Insère l'élément o dans v à la position i.
v.removeElement(i) Supprime l'élément de v à la position i.
v.removeAllElements() Supprime tous les éléments de v.
v.size() Retourne le nombre d'éléments de v.
v.setSize(n) Redimensionne v à la taille n.
v.indexOf(o) Retourne la position où se trouve l'élément de valeur o, si aucun n'est trouvé, -1 est retourné.
v.elements() Retourne une énumération des éléments de v (cf. la dernière section).
 
 
Dictionnaire
 

Le dictionnaire, la classe Dictionary, équivalente à la classe map de la STL, représente un conteneur associatif, où un élément est identifié par une clé. Voici un exemple simple qui associe à des entiers leur forme textuelle.

Dictionary d = new Hashtable();

d.put(new Integer(1),"un");
d.put(new Integer(2),"deux");
d.put(new Integer(3),"trois");
...

La classe Dictionary est abstraite, la sous-classe que nous utilisons ici est Hashtable. Pour récupérer ensuite la forme textuelle d'un nombre, il suffit de fournir la clé, i.e. le nombre, au dictionnaire.

String s = (String)d.get(new Integer(3));

La méthode get renvoie null si la clé n'est pas présente dans le dictionnaire. Il faut noter également que get retourne une référence sur un objet de type Object, et qu'il est nécessaire de le convertir, dans notre exemple en une référence sur un objet de type String, la syntaxe pour cela est identique à celle du C++.

Il est également possible de supprimer un élément du dictionnaire, en utilisant sa clé.

d.remove(new Integer(3));

La méthode size permet de connaître la taille d'un dictionnaire.

int taille = d.size();

Suivant les circonstances, il peut être intéressant de parcourir tous les éléments du dictionnaire ou toutes ses clés. Pour cela, le dictionnaire dispose de deux méthodes: keys qui renvoie une énumération des clés, et elements qui renvoie une énumération des éléments. La notion d'énumération est présentée dans la section suivante.

 
Enumération
 

Les conteneurs ne fournissent pas d'interface commune pour parcourir tous les éléments qu'ils contiennent, ce qui est nécessaire pour que les algorithmes soient indépendants des structures de données qu'ils manipulent. En outre, un conteneur comme le dictionnaire possède deux ensembles d'éléments, les clés et les valeurs associées. Pour disposer d'une manière commune de parcourir un ensemble d'éléments, indépendamment du type de conteneur, il existe la classe (ou plus exactement l'interface) Enumeration qui représente un ensemble ordonné d'éléments. Un conteneur dispose de méthodes permettant de le convertir en un objet de la classe Enumération (cf. les méthodes keys et elements des conteneurs Vector et Dictionary). Reprenons l'exemple de la section précédente pour parcourir les clés du dictionnaire.

Enumeration e = d.keys();

while (e.hasMoreElements()) {
 i=(Integer)e.nextElement();
 ...
}

 
 
Copyright (c) 1999-2016 - Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
La permission est accordée de copier, distribuer et/ou modifier ce document sous les termes de la licence GNU Free Documentation License, Version 1.1 ou toute version ultérieure publiée par la fondation Free Software Foundation. Voir cette licence pour plus de détails (http://www.gnu.org).