Besoin de renseignement? Contactez nous....
Envoyer un Email +33 7 81 01 72 30
contact@odellya.com

Débuter avec Java – Les types composés

 

 

Objectifs : Apprendre comment créer et manipuler les types composés.

Ce tutoriel va être consacré à la découverte des types composés qui sont les types objet déjà prédéfinis par java, dont le String, le StringBuildemr, le StringBuffer, les arrays (tableau), les Vector, les List et les ArrayList. Ici on va apprendre comment créer, modifier et supprimer ces objets.

  1. Les chaines de caractères :

1. Le type String :


On rappelle que le type String représente les chaines de caractères.

C’est un type prédéfini placé dans le package java.lang de la Java API.

  • Déclaration :

   String chaine ;

  • Initialisations:
  1. chaine =new String (« tutoriel « ) ;

Ou

  1. chaine = « tutoriel  » ;

L’initialisation n°1 permet de créer un objet de type String, dont la référence « chaine » pointe sur un référant (objet) stocké dans la mémoire contenant la valeur « tutoriel ».

L’initialisation n°2 permet de créer un objet de type String, dont la référence « chaine » pointe sur un référant (objet) stocké dans le « pool » des String contenant la valeur « tutoriel ».

  • Comparaison de deux variables String :

1ère comparaison :

String ch1= new String(« tutoriel « ) ;

String ch2= new String(« tutoriel « ) ;

On peut remarquer qu’à chaque instanciation un nouvel objet se crée même si sa valeur existe déjà en mémoire.  De plus on peut également rappeler l’opérateur == compare les adresses pointées par les variables et non leurs valeurs.

Par contre avec la méthode equals() on compare la valeur de 2 objets de type String.

Remarque : La méthode equals() de la classe Object permet de comparer les adresses mémoires de 2 référents mais la classe String a redéfini la méthode par l’implémentation d’un corps qui permet la comparaison des valeurs de 2 objets de type String.

2eme comparaison :

String ch1= « tutoriel »;
String ch1= « tutoriel »;

La réponse retournée est true vu que les variables pointent sur le même objet du pool donc ils ont la même référence. Et par suite le résultat de la comparaison avec la fonction equals sera évidemment true.

Pour résumer, si l’objet String est créé avec le mot clé new alors c’est toujours un nouvel objet String qui se crée par contre si l’objet String est créé avec l’opérateur = alors un nouvel objet String se crée uniquement si un objet String avec la valeur de ce dernier n’existe pas déjà dans le pool.

2. StringBuilder :


Comme le type String, le StringBuilder sert à créer un objet de type chaine de caractère sauf que ce dernier est utilisé lors du besoin d’une modification exercée automatiquement sur la chaine. Prenons un exemple pour comprendre :

On remarque bien que la concaténation et l’affectation sont faites implicitement sur la variable ch2 contrairement à la variable ch1 qui nécessite une affectation explicite pour forcer la modification et dans ce cas la variable ch1change de réfèrent (elle ne pointe plus sur le même objet).

3. StringBuffer :


Similaire à StringBuilder sauf que la différence se situe au niveau du mode de fonctionnement synchrone et asynchrone, ça c’est en rapport avec les Threads.

Il faut noter aussi qu’un objet String est réellement un tableau de char chaque case prend un caractère de la chaine saisie et que ce tableau est déclaré final ce qui le rend immutable (ne tolère pas la modification) contrairement à StringBuffer et StringBuilder.

Il faut juste retenir que pour choisir le type approprié parmi ces 3 on doit passer par ces 4 critères :

  • Si le traitement de votre chaine ne nécessite pas de lui apporter des modifications utilisez la classe String car un objet String est immutable.
  • Si votre chaine nécessite des modifications et qu’il sera accessible par un seul Thread, dans ce cas utilisez StringBuilder qui fonctionne en mode asynchrone
  • Si votre chaine nécessite des modifications et qu’il sera accessible par plusieurs Thread, dans ce cas utilisez StringBuffer qui fonctionne en mode synchrone.

Notion de Thread : Tout ce qu’on peut dire d’un thread à ce niveau-là, c’est qu’ils sont des processus légers qui peuvent s’exécuter « simultanément » et peuvent partager les mêmes ressources selon la priorité de chacun.

On étudiera les threads plus en détails dans un niveau plus avancé de Java.

II. Les tableaux :

On rappelle qu’un tableau est un objet qui permet de stocker un ensemble de valeurs. Il peut stocker un ensemble de données de type primitif comme un ensemble d’objets. Et ce tableau peut être un tableau à une seule dimension ou à multiple dimensions.

 1. Tableau à une seule dimension :


  • Déclaration :

Il existe plusieurs manières de déclarer un tableau :

typenom_tab[ ] = new type [taille] ;

type[ ]nom_tab= new type [taille;

type[ ]nom_tab= new type [ ]{valeur1,valeur2,valeur3} ;

typenom_tab[ ] ;

type[ ]nom_tab;

Prenons un exemple :

L’indice des éléments du tableau commence de 0 jusqu’à taille (tableau) -1, dans notre cas de 0 à 2 car ici la taille du tableau est connu, si jamais on l’ignore l’attribut tab.length nous retourne le nombre de cases du tableau.

tab[0] : indique le premier élément du tableau, 0 c’est l’indice de la première case.

Pour ce qui est entouré en jaune, comme on le sait déjà, quand on déclare un attribut il prend une valeur par défaut selon son type. De même ici un tableau d’entiers après sa création des cases prennent 0 jusqu’à une affectation ultérieure, ce qui explique tab[1]=0 ;

Remarque : 

Nous voyons bien qu’il nous est interdit d’initialiser un tableau qui n’est pas créé. Tout à fait logique ! Il n’y a aucune case réservée pour le tab4 pour y mettre une valeur. La solution est la suivante :

Si on désire remplir un tableau avec des données de types différents il faut qu’on déclare le tableau avec le type Object qui remplace tout type soit-il primitif ou objet.

Essayons de déclarer un tableau contenant int, boolean, char, float, et un objet String :

De cet exemple on peut constater qu’un tableau peut contenir des types primitifs variés comme il peut contenir des objets. String est une classe prédéfinie mais aussi on peut créer nos propres classes et remplir un tableau avec des instances de cette dernière :

  • Manipulation :

Pour un tableau on ne peut qu’afficher et modifier les valeurs stockées, la suppression est interdite car la taille d’un tableau est fixée dès sa création et est inchangée jusqu’à sa destruction.

Cet exemple montre comment manipuler un objet qui possède des attributs et des méthodes.

On a déclaré les attributs nom et age avec une portée private pour une raison d’encapsulation et c’est à travers les getters et les setters qu’on peut y accéder.

Ici tab[0].getNom() : sert à afficher le nom de l’objet (type Personne) stocké dans la première case du tableau (dont l’indice est 0).

tab[0].setAge(17) : permet de modifier l’âge de l’objet (type Personne) stocké dans la première case du tableau (dont l’indice est 0).

Retenons qu’un tableau est un objet qui pointe sur un ensemble soit de types primitifs dans ce cas les cases stockeront des valeurs soit un ensemble d’objets et là les cases auront à stocker les références (adresses mémoire) des objets créés.

2. Tableau multidimensionnel :


Un tableau multidimensionnel, on peut le décrire comme étant un tableau de tableau. Par exemple un tableau à 2 dimensions est un objet qui pointe sur un groupe de tableaux à une seule dimension et un tableau à 3 dimensions est un objet qui pointe sur un groupe de tableaux à 2 dimensions et ainsi de suite … On parle d’une matrice de dimensions (n,m).

  • Déclaration :

Il existe plusieurs manières de déclarer un tableau multidimensionnel :

typenom_tab[ ][ ]= new type [nombre lignes][ ] ;

type[ ][ ]nom_tab= new type [nombre lignes][ ] ;

type[ ]nom_tab[ ] = new type [nombre lignes][ ] ;

type[ ]nom_tab[ ]= new type [nombre lignes][nombre colonnes] ;

type[ ]nom_tab= new type [ ][ ]{{valeur1,valeur2,valeur3},{ valeur3,valeur4}} ;

typenom_tab[ ][ ] ;

type[ ][ ]nom_tab ;

Prenons un exemple de déclaration et d’initialisation d’un tableau de 2 dimensions avec 3 lignes et 4 colonnes ça veut dire prenons un exemple de déclaration et d’initialisation d’un tableau qui pointe sur une collection de 3 tableau d’entiers chacun à 4 cases :

tab[0][1] : signifie la 2eme case du 1er tableau.

Si on désire que les tableaux ne possèdent pas tous le même nombre d’élément, on devra créé notre tableau pointeur sans préciser le nombre de colonnes et puis pour chaque élément (tableau) de la collection on construit le tableau à l’aide du mot clé new pour préciser le nombre de cases. Ça sera comme suit :

III. ArrayList et Vector :

  1. ArrayList :

Tout comme le tableau, un ArrayList est un objet qui permet de stocker une collection de valeurs sauf que celui-ci est plus flexible vu qu’on n’a pas de contrainte de taille fixe. Avec un ArrayList on peut ajouter, modifier et supprimer un élément sans aucun problème, ce qui nous fait gagner en termes d’allocation de mémoire par rapport à l’utilisation du tableau.

La classe ArrayList est définie dans le package java.util et implémente l’interface List.

  • Déclaration :

ArrayList<type>nom_liste= new ArrayList<type> () ;

ArrayList<type>nom_liste= new ArrayList<> () ;

ArrayList<type>nom_liste ;

Remarque : un ArrayList ne peut contenir que des objets, les types primitifs ne sont pas acceptés.

Si on désire remplir un ArrayList par des valeurs de types primitifs : byte, short, int, long, float, double, char et boolean, on utilisera le classe wrapper correspondant soit Byte, Short, Integer, Long, Float, Double, Character et Boolean pour préciser le type des éléments de la liste au moment de sa création.

  • Manipulation :

Nous allons prendre un exemple pour créer et remplir un ArrayList puis retirer, modifier un élément et à chaque fois vérifier la taille :

On a créé une liste d’objets Character (caractères).

On a créé 3 objets Character.

A l’aide de la méthode add(Character c) nous avons stockés les 3 objets créés dans la liste. Comme on a pu aussi insérer directement une valeur char (type primitif) add(char c)  et ça s’explique par la conversion automatique du type primitif char à sa classe wrapper Character, on appelle cette opération autoboxing (la conversion inverse s’appelle unboxing). Ce schème illustre comment sont organisés les éléments qu’on vient d’insérer dans la liste :

remove(int x) nous permet d’éliminer un élément de la liste par sa position.

get(int x).getClass() sert à connaitre le type (classe) de l’objet de la position x de la liste. Cette fonction est bien utile quand il s’agit d’une liste qui contient des éléments de types différents autrement dit quand la liste est déclarée avec le type Object.

set(int x, char c) ou set(int x, Character c) permet de modifier le contenu de l’élément de la position x avec une valeur c.

2. Vector :

Même principe que l’ArrayList, un Vector est aussi un objet qui sert à sauvegarder une collection d’objets.

  • Déclaration :

Vector<type>nom_vec= new Vector<type>() ;

Vector<type>nom_vec= newVector<> () ;

Vector<type>nom_vec ;

Voici un exemple :

On voit bien que c’est le même principe que pour les ArrayList, les seules différences résident dans le mode de synchronisation toujours quand on part d’un accès concurrent (thread), un Vector est synchronisé alors qu’un ArrayList ne l’est pas et une différence de redimensionnement expliquons ce point : l’ArrayList se fait croitre de la moitié de sa taille actuelle alors qu’un vector croit du double de sa taille.

  • Comparaison de 2 liste VS comparaison de 2 tableaux :

2 tableaux et 2 listes qui contiennent par les mêmes objets. Une fois avec l’opérateur == et une fois avec la méthode equals(). Ces comparaisons nous donnent les résultats suivants :

Pour les tableaux comme pour les listes la comparaison par == donne false.

Explication : Revenons à la définition d’un tableau et d’une liste, tous 2 sont des objets et tout objet créé possède une adresse de mémoire. De plus on sait que l’opérateur == compare les adresses des objets comparés ce qui explique le résultat false dû à l’inégalité des adresses des 2 tableaux et des 2 listes.

Pour les tableaux la comparaison par equals() donne false par contre pour les liste cette dernière donne true.

Explication : Par défaut dans la classe Object la méthode equals() compare les adresses mémoire des objets référencés mais il y a certaine classe filles qui ont redéfinie cette méthode pour la rendre une méthode de comparaison des valeurs comme la classe String et la classe List, d’où la comparaison des 2 ArrayListes est une comparaison 2 par 2 (liste.get(0) avec liste1.get(0) … )les références (adresses mémoires) des objets Personnes qu’elles stockent ce qui a retourné true vu que les 2 listes pointent sur les mêmes objets.

Remarque :

Dans le cas d’une liste, equals() ne compare pas les références des 2 listes comparées mais aussi ne compare pas les valeurs (les valeurs des attributs) des objets qu’elles stockent plutôt elle compare les valeurs des adresses mémoires (références) de ces objets. Ce qui explique le false retourne.

IV. La boucle for, la boucle foreach et iterator:

for, foreach et iterator servent tous à parcourir un tableau ou une liste et bien sur chacune à sa façon et chacune a sa particularité.

  • for :

On a déjà vu la syntaxe de la boucle for et les paramètres qu’elle peut prendre, cette fois on va pour parcourir une liste et un tableau :

Grace à la boucle for compteur i qui se pointe sur chaque élément de la liste commençant par 0 jusqu’à liste.size()-1 (précisé par  i<liste.size()) , on l’a parcouru pour afficher le nom de chaque objet Personne stocké puis avec la même boucle de 0 à tab.length-1 (précisé par  i<tab.length) on a parcouru le tableau pour afficher l’âge de chaque Objet Personne stocké dans ce dernier.

  • foreach :

foreach c’est une boucle for améliorée, destinée à parcourir les élément d’une collection ou d’un tableau et diminue la quantité de code écrits et en voilà un exemple :

Son principe repose sur la déclaration d’une variable du même type que les éléments de la collection ou du tableau et utiliser cette variable comme une étiquette sur l’élément au lieu de liste1.get(i).getAge() ça devient p.getAge() à la position i, le passage d’un élément à un autre sera automatique (i++).

Remarque :

Quant à la modification via la boucle foreach elle n’affecte pas tout type d’élément et en voilà la preuve :

On remarque bien que la modification de l’âge a été effectuée avec succès, la valeur 20 est remplacée par la valeur 21. Par contre les entiers normalement incrémentés par 1 sont inchangés.

On peut donc conclure que la modification via la boucle foreach ne s’effectue réellement que sur les valeurs des objets, et n’aura aucun impact sur les valeurs de types primitifs, et ceci expliqué par le type de passage de la valeur de l’élément à la variable de la boucle (p, n). Passage par valeur dans le cas d’élément de type primitif et le passage par référence dans le cas des objets.

  • Iterator :

Est une interface qui fournit des méthodes permettant de parcourir un objet de type Collection (List, ArrayList, Vector..). Etendue par l’interface Collection et implémentée par ses classes dérivées.

On va voir 2 exemples un avec la boucle while et l’autre avec la boucle for :

On a déclaré une référence de type Iterator<Personne> pour lui affecté un objet
iterator sur les éléments de cette liste.

Comme on sait pour une liste ou une file chaque élément pointe sur l’élément suivant et le dernier pointe sur null. Donc à l’aide de la méthode hasNext() qui retourne vrai si l’élément actuel i possède un successeur et pointe sur ce dernier sinon faux quand rencontre la valeur null (fin de la liste) et la méthode next() retourne l’élément suivant se fait le parcours de tous les éléments.

Maintenant passons à la boucle for :

A l’entête de la boucle for on a rempli seulement 2 champs le champs de l’initialisation et celui de la condition d’arrêt, le dernier champs celui du pas d’avancement est vide tout simplement parce que le passage à l’élément suivant se fait implicitement quand la méthode hasNext() retourne vrai.

Voilà c’est fini pour aujourd’hui, on a vu les différents types composés et ainsi que leurs méthodes de manipulations, j’espère que tout est clair  pour vous.

Je vous donne rendez-vous très prochainement pour découvrir les exceptions

Merci de nous avoir fait confiance et je vous invite à consulter quotidiennement le site d’Odellya pour profiter des prochains tutoriels qui seront bientôt à votre disposition.

 

 

Si ce tutoriel vous a été utile, soyez généreux, pensez aux autres en le partageant dans les réseaux sociaux. Il vous suffit de cliquer sur les icônes ci-dessous.