TD3 : tableaux avancées, première classe et chaı̂nes de caractères 1 Les tableaux 1.1 Éléments théoriques Déclaration des tableaux Pour la déclaration des tableaux, deux notations sont possibles. La première notation, place une paire de crochet par dimension du tableau juste après un type : int[] unTableau; double[][] unTableauBidimensionnel; La seconde déclaration place la (ou les) paire(s) de crochets après le nom de la variable liée. int unTableau[]; double unTableauBidimensionnel[][]; Construction des tableaux La construction se fait en utilisant l’opérateur new suivi du type de donnée contenues dans le tableau puis de la taille voulue pour chaque dimensions entre crochets. unTableau = new int[10]; unTableauBidimensionnel = new double[5][23]; Les tableaux peuvent aussi être construit en plusieurs étapes. Les dimensions doivent être spécifiées de “la gauche vers la droite”. Chaque “sous tableau” est alors un tableau de dimension inférieure. L’exemple ci dessous présente la construction d’un tableau bidimensionnel triangulaire : src/TableauBidim.java Chaque ligne est expliquée ci dessous : – Le tableau donnees est déclaré bidimensionnel. – Le tableau est partiellement construit, seule la première dimension est fixée à 10. tableau[i] est un tableau mono-dimensionnel (dimension 2 − 1) dont la taille n’est pour l’instant pas fixée. – La boucle crée un tableau mono-dimensionnel pour chaque tableau[i] dont la taille vaut [i+1] – Deux affectations sont ensuite effectuées. Le tableau obtenu est donc triangulaire composé de 1 élément sur la première ligne, 2 sur la deuxième,. . . Tableaux contenant des objets Les tableaux peuvent aussi contenir des objets. La déclaration du tableaux est la même, un type (dans ce cas une classe) suivit du 1 nom du tableau : String unTableau[]; String unTableauBidimensionnel[][]; La construction du tableau fait appel au mot clé new suivit du nom de la classe et de (ou des) paire(s) de crochets contenant les dimensions. unTableau = new String[10]; unTableauBidimensionnel = new String[5][23]; A la suite de la construction du tableau, les éléments contenus doivent être crées un à un, souvent à l’aide d’une boucle. L’exemple suivant présente la construction d’un tableau de chaı̂ne de caractères : String tab[]; tab = new String[10]; for (int i = 0; i < 10; i++) { tab[i] = new String(); } Remarque Souvent, la déclaration et la construction du tableau sont faites en une seule ligne : int unTableau[] = new int[10]; double unTableauBidimensionnel[][] = new double[5][23] 1.2 Initialisation aléatoire d’une matrice Écrivez un programme qui initialise une matrice de taille 10 par 5 avec des valeurs aléatoires comprises entre 0 et 10. Correction : import java.util.Random; public class InitialisationAleatoire { public static void main(String[] args) { int[][] matrice = new int[5][10]; Random gen = new Random(); // Remplissage de la matrice for (int i = 0; i < matrice.length; i++) { for (int j = 0; j < matrice[0].length; j++) { matrice[i][j] = gen.nextInt(10); } } // Affichage de la matrice for (int i = 0; i < matrice.length; i++) { 2 for (int j = 0; j < matrice[0].length; j++) { System.out.print(matrice[i][j]+" " ); } System.out.println(); } } } 1.3 Somme de matrices A partir du programme précédent, écrivez un programme qui calcule la somme de deux matrices remplies aléatoirement. Correction : import java.util.Random; public class SommeMatrices { public static void main(String[] args) { int[][] matrice1 = new int[5][10]; int[][] matrice2 = new int[5][10]; int[][] somme = new int[5][10]; Random gen = new Random(); // Remplissage de la matrice for (int i = 0; i < matrice1.length; i++) { for (int j = 0; j < matrice1[0].length; j++) { matrice1[i][j] = gen.nextInt(10); matrice2[i][j] = gen.nextInt(10); } } // Calcul de la somme for (int i = 0; i < matrice1.length; i++) { for (int j = 0; j < matrice1[0].length; j++) { somme[i][j] = matrice1[i][j] + matrice2[i][j] ; } } // Affichage du resultat for (int i = 0; i < somme.length; i++) { for (int j = 0; j < somme[0].length; j++) { System.out.print(somme[i][j]+" " ); } System.out.println(); } 3 } } 1.4 Produit de matrices Écrivez un programme qui initialise aléatoirement deux matrices, l’une de 5×3 et l’autre de 3 × 5 éléments. A partir du programme précédent, écrivez un programme qui calcule le produit des deux matrices. Correction : import java.util.Random; public class ProduitMatrices { public static void main(String[] args) { int[][] matrice1 = new int[5][3]; int[][] matrice2 = new int[3][5]; int[][] produit = new int[5][5]; Random gen = new Random(); // Remplissage de la matrice for (int i = 0; i < matrice1.length; i++) { for (int j = 0; j < matrice1[0].length; j++) { matrice1[i][j] = gen.nextInt(10); matrice2[j][i] = gen.nextInt(10); } } // Calcul du produit for (int ligne = 0; ligne < matrice1.length ; ligne++) { for (int colonne = 0; colonne < matrice2[0].length; colonne++) { produit[ligne][colonne] = 0 ; for (int m = 0; m < matrice1[0].length; m++) { produit[ligne][colonne] = produit[ligne][colonne] + ( matrice1[ligne][m] * matrice2[m][colonne]); } } } // Affichage du resultat for (int i = 0; i < produit.length; i++) { for (int j = 0; j < produit[0].length; j++) { System.out.print(produit[i][j]+" " ); } System.out.println(); } 4 } } 1.5 Classe matrice Vous allez construire une classe Matrice dont le schéma UML est présenté dans la figure ci dessous. Matrice - données : entier[][] + Matrice() + Matrice(Matrice uneMatrice) + Matrice(entier[][] unTableau) + Matrice(entier nbLignes,entier nbColonnes) + getNombreLignes() : entier + getNombreColonnes() : entier + getDonnées(entier ligne,entier colonne) : entier + setDonnées(entier ligne, entier colonne, entier uneValeur) : + RemplirMatriceAléatoirement() : + AfficherMatrice() : + CalculSomme(Matrice uneMatrice) : Matrice + CalculProduit(Matrice uneMatrice) : Matrice + CalculDeterminant() : entier F IG . 1 – Schéma UML de la classe Matrice 1.5.1 Attributs La classe matrice a un seul attribut, un tableau bidimensionnel qui contient les valeurs de la matrice. 1.5.2 Méthodes Les constructeurs La classe comporte quatre constructeurs : – le premier constructeur ne reçoit aucun paramètre, il est vide. – le second constructeur reçoit une matrice en paramètre et en construit une copie. – le troisième constructeur reçoit un tableau d’entiers et construit une matrice à partir des valeurs contenues dans le tableau. – le quatrième constructeur construit une matrice à partir du nombre de lignes et du nombre de colonnes passés en paramètre. 5 La méthode d’affichage La méthode AfficherMatrice permet d’afficher le contenu de la matrice en utilisant System.out.print et System.out.println et de tabulation (\t). La méthode de remplissage La méthode RemplirMatriceAleatoirement permet de remplir aléatoirement une matrice avec des valeurs comprises entre 0 et 9 (inclus). Les assesseurs Les attributs sont privés. Les données doivent donc être lues ou modifiées en passant par des méthodes1 . Deux méthodes permettent de déterminer les dimensions de la matrice : – la méthode getNombreLignes renvoie le nombre de lignes contenues dans la matrice – de même, la méthode getNombreColonnes renvoie le nombre de colonnes contenues dans la matrice. Deux méthodes permettent de lire/écrire les données contenues dans la matrice : – getValeur renvoie la valeur contenue aux coordonnées passées en arguments d’appel – La méthode setValeur permet de placer une valeur dans la matrice (la valeur et les coordonnées sont passées par les arguments d’appel). Ces deux méthodes vérifient que les coordonnées sont valides avant d’effectuer une opération de lecture ou d’écriture. Méthodes arithmétiques Deux méthodes permettent d’opérer des calculs arithmétiques sur des matrices. Ces deux méthodes reçoivent une matrice en paramètre et renvoie une nouvelle matrice construite à partir de la matrice considérée (référencée par this) et celle passée en paramètres. – CalculSomme renvoie la somme – CalculProduit renvoie le produit Si les calculs ne sont pas possibles (tailles des matrices non compatibles,. . . ) ces méthodes renvoient null Calcul du déterminant La méthode CalculDeterminant permet de calculer le déterminant d’une matrice. Une méthode relativement facile à programmer est le calcul récursif. – si la matrice a une taille de 1 × 1, le déterminant est la valeur contenue dans la matrice, – sinon, on calcul le déterminant de manière récursive. Exemple pour une matrice 3×3 a0,0 a0,1 a0,2 A = a1,0 a1,1 a1,2 a2,0 a2,1 a2,2 Le déterminant se calcul de la manière suivante : 1 Les méthodes permettant d’accéder à des données privées sont des assesseurs 6 a1,1 a1,2 a1,0 a1,2 a1,1 a1,2 + a0,2 . det − a0,1 . det det A = a0,0 . det a2,1 a2,2 a2,0 a2,2 a2,1 a2,2 puis a a det 1,1 1,2 a2,1 a2,2 ... = a1,1 . det a2,2 − a1,2 det a2,1 Correction : import java.util.Random; public class Matrice { /** * Les donnees contenues dans la matrice */ public int[][] donnees; /** * Constructeur de base */ public Matrice() { } /** * Construction d’une matrice a partir de ses dimensions * * @param nbLignes le nombre de lignes * @param nbColonnes le nombre de colonnes */ public Matrice (int nbLignes, int nbColonnes){ if ( (nbLignes != 0) && (nbColonnes != 0) ){ this.donnees = new int[nbLignes][nbColonnes]; } } /** * Construction d’une matrice a partir d’un tableau * * @param unTableau les donnees sources */ public Matrice (int[][] unTableau){ if ( (unTableau.length != 0) && (unTableau[0].length != 0) ){ 7 this.donnees = new int[unTableau.length][unTableau[0].length]; for (int i = 0; i < unTableau.length; i++) { for (int j = 0; j < unTableau[0].length; j++) { this.donnees[i][j] = unTableau[i][j]; } } } } /** * Construit une matrice en copiant une matrice existant * * @param uneMatrice la matrice source */ public Matrice (Matrice uneMatrice){ this.donnees = new int[uneMatrice.donnees.length][uneMatrice.donnees[0].length]; for (int i = 0; i < donnees.length; i++) { for (int j = 0; j < donnees[0].length; j++) { this.donnees[i][j] = uneMatrice.donnees[i][j]; } } } /** * Renvoie le nombre de lignes composant la matrice * * @return le nombre de lignes */ public int getNombreLignes(){ return this.donnees.length; } /** * Renvoie le nombre de colonnes composant la matrice * * @return le nombre de colonne */ public int getNombreColonnes(){ return this.donnees[0].length; } /** * Permet de lire un element de la matrice * * @param ligne l’abcisse de la cellule voulue 8 * @param colonne l’ordonnee de la cellule voulue * @return la valeur de cet element */ public int getDonnees(int ligne, int colonne) { if( (ligne<this.donnees.length) && (colonne<this.donnees[0].length) ){ return this.donnees[ligne][colonne]; }else{ return 0; } } /** * Permet d’ecrire un element dans la matrice * * @param ligne l’abcisse de la cellule voulue * @param colonne l’ordonnee de la cellule voulue * @param uneValeur la valeur a ecrire */ public void setDonnees(int ligne, int colonne, int uneValeur) { if( (ligne<this.donnees.length) && (colonne<this.donnees[0].length) ){ this.donnees[ligne][colonne] = uneValeur; } } /** * Remplissage aleatoire de la matrice avec des valeurs comprises en 0 et 9 */ public void RemplirMatriceAleatoirement(){ Random generateurAleatoire = new Random(); for (int i = 0; i < this.donnees.length; i++) { for (int j = 0; j < this.donnees[0].length; j++) { this.donnees[i][j] = generateurAleatoire.nextInt(10); } } } /** * Affiche le contenu de la matrice */ public void AfficherMatrice(){ for (int i = 0; i < this.donnees.length; i++) { for (int j = 0; j < this.donnees[0].length; j++) { System.out.print("\t"+this.donnees[i][j]); } 9 System.out.println(); } } /** * Calcul la somme de la matrice actuelle avec la matrice passee en parametre * * @param uneMatrice la matrice a ajouter * @return la somme matricielle */ public Matrice CalculSomme(Matrice uneMatrice){ Matrice Somme = null; if( (this.donnees.length == uneMatrice.donnees.length) && (this.donnees[0].length == uneMatrice.donnees[0].length) ){ Somme = new Matrice (this.donnees.length,this.donnees[0].length); for (int ligne = 0; ligne < this.donnees.length ; ligne++) { for (int colonne = 0; colonne < this.donnees[0].length; colonne++) { Somme.donnees[ligne][colonne] = this.donnees[ligne][colonne] + uneMatrice.donnees[ligne][colonne]; } } } return Somme; } /** * Calcul le produit de la matrice actuelle avec la matrice passee en parametre * * @param uneMatrice la matrice a multiplier * @return le produit matriciel */ public Matrice CalculProduit(Matrice uneMatrice){ Matrice Produit = null; if( (this.donnees.length == uneMatrice.donnees[0].length) && (this.donnees[0].length == uneMatrice.donnees.length) ){ Produit = new Matrice(this.donnees.length,uneMatrice.donnees[0].length); 10 for (int ligne = 0; ligne < this.donnees.length ; ligne++) { for (int colonne = 0; colonne < uneMatrice.donnees[0].length; colonne++) { Produit.donnees[ligne][colonne] = 0 ; for (int m = 0; m < this.donnees[0].length; m++) { Produit.donnees[ligne][colonne] = Produit.donnees[ligne][colonne] + ( this.donnees[ligne][m] * uneMatrice.donnees[m][colonne]); } } } } return Produit; } /** * Calcul le determinant de la matrice de maniere recursive * * @return le determinant de la matrice */ public int CalculerDeterminant(){ int det = 0; if ( this.donnees.length == this.donnees[0].length ){ if ( (this.donnees.length == 1 ) && (this.donnees[0].length == 1)){ det = this.donnees[0][0]; }else{ /* Parcours de la premiere ligne */ for (int colonne = 0; colonne < this.donnees[0].length; colonne++) { /* Creation du sous tableau */ int[][] sousTableau = new int[this.donnees.length 1][this.donnees[0].length - 1]; /* Remplissage du sous tableau */ for (int ligneTableau = 1; ligneTableau < this.donnees.length; ligneTableau++) { for (int colonneTableau = 0; colonneTableau < this.donnees[0].length; colonneTableau++) { if (colonneTableau<colonne){ sousTableau[ligneTableau-1][colonneTableau] = 11 this.donnees[ligneTableau][colonneTableau]; }else{ if (colonneTableau>colonne){ sousTableau[ligneTableau-1][colonneTableau-1] = this.donnees[ligneTableau][colonneTableau]; } } } } /* Creation de la sous matrice a partir du sous tableau */ Matrice sousMatrice; sousMatrice = new Matrice(sousTableau); /* Alternance de signe */ if(colonne % 2 ==0){ det = det + this.donnees[0][colonne] * sousMatrice.CalculerDeterminant(); }else{ det = det - this.donnees[0][colonne] * sousMatrice.CalculerDeterminant(); } } } } return det; } } 1.6 Classe de test Écrivez une classe de test simple (juste une méthode main) pour tester votre classe Matrice. Correction : public class TestMatrice { /** * @param args */ public static void main(String[] args) { /*Matrice A,B,C; A = new Matrice(2,3); 12 B = new Matrice(3,2); A.RemplirMatriceAleatoirement(); B.RemplirMatriceAleatoirement(); System.out.println("A : "); A.AfficherMatrice(); System.out.println("B : "); B.AfficherMatrice(); System.out.println("Produit : "); C=A.CalculProduit(B); C.AfficherMatrice();*/ Matrice E; E = new Matrice(3,3); E.RemplirMatriceAleatoirement(); E.AfficherMatrice(); System.out.println("--- Determinant ---"); System.out.println(E.CalculerDeterminant()); } } 2 Les chaı̂nes 2.1 Éléments théoriques Les chaı̂nes de caractères sont représentées par différentes classes. La plus utilisée est la classe String. Son comportement est particulier et doit être bien compris pour éviter de “mauvaises surprises” lors du développement de programme. La classe String est déclarée final dans le langage. Il est donc impossible de créer une classe fille. Construction de chaı̂ne La construction des chaı̂nes est simplifiée dans la langage. Il n’est pas nécessaire d’appeler directement le constructeur (c’est le seul cas dans le langage Java !). String maChaine; maChaine = "Bonjour"; Dans la première ligne la référence est déclarée puis dans la deuxième ligne elle est assigné à l’objet String contenant la chaı̂ne Bonjour. Elle peut être écrite (c’est normalement l’usage) en une ligne : String maChaine = "Bonjour"; 13 Les chaı̂nes peuvent être mise bout-à-bout (concaténées) en utilisant l’opérateur +. Il s’agit en fait d’une notation simplifiée évitant l’appel de la méthode concat de la classe String. String un = "Un"; String deux = " Deux"; String trois = un + deux; Méthodes utiles de la classe String – Pour obtenir le nombre d’élément (caractères) dans la chaı̂ne, on utilise la méthode length(). – La méthode char charAt(int index) renvoie le caractère situé à la position donnée par index, s’il existe. – int indexOf(String str) permet de recherche la position (renvoyée par l’entier) de la chaı̂ne str dans la chaı̂ne utilisée. – Deux chaı̂nes peuvent être comparées en utilisant la méthode equals : a.equals(‘‘Toto’’). – Il est aussi possible de les comparer en ignorant la casse (majuscule/minuscule) avec la méthode equalsIgnoreCase. – Les méthodes startsWith et endsWith permettent de vérifier si une chaı̂ne contient une sous chaı̂nes en début/fin de la chaı̂ne considérée. L’extrait ci-dessus présente l’utilisation de deux de ces méthodes : src/ExempleChaine.java Dans la document Java on trouve une méthode surchargée déclarée ainsi : static String valueOf(...) Cette méthode permet de convertir un type primitif en chaı̂ne de caractères (entier, flottant,. . . ). Comportement particulier des String Les chaı̂nes sont immuables. Toutes les fonctions qui renvoient une chaı̂ne renvoient un nouvel objet. String c=’’Bonjour’’; String b; b=c; // b et c designent le meme objet c=c.toLowerCase(); // b et c ne designent plus le meme objet 2.2 Palindrome Écrivez un programme qui demande un mot à l’utilisateur (la classe Scanner propose une méthode nextLine() qui renvoie une chaine String) et vérifie si ce mot est un palindrome. Un palindrome est un mot qui peut être lu de manière identique de droite à gauche et de gauche à droite (comme, par exemple, le mot kayak). Correction : 14 import java.util.Scanner; public class Palindrome { public static void main(String[] args) { Scanner scan = new Scanner(System.in); String texte; boolean pal; int i=0; System.out.println("Tapez un mot : "); texte = scan.nextLine(); do{ if (texte.charAt(i)!= texte.charAt(texte.length()-i-1)){ pal = false; }else{ pal = true; } i++; }while( (i<texte.length()) && pal); if (pal){ System.out.println(texte + " est un palindrome"); }else{ System.out.println(texte + " n’est pas un palindrome"); } } } 15