Java, les bases


Les tableaux en Java (1)

Les tableaux à une dimension

Cet article décrit comment utiliser les tableaux en langage Java. Seul seront traités les tableaux à une dimension. Les tableaux à plusieurs dimensisons seront traités dans un article spécifique.

Comme dans la plupart des langages, Java permet de gérer un groupe de données de même types sous forme de tableau dont chaque élément est accessible à partir d’un indice. Dans cette première partie, nous aborderons les tableaux à une dimension.


 Introduction

En Java, le type Tableau n’est pas une classe et pourtant, une instance de Tableau est bien un objet qui hérite de java.lang.Object, il est créé sur le tas à partir de l’opérateur new et on y accède par une référence.

Les tableaux implémentent les interfaces Serializable et Clonable et redéfinissent la méthode clone(). Contrairement aux collections, ils ne peuvent pas être redimensionnés une fois créés mais un tableau peut contenir aussi bien des objets que des types primitifs

 Création d’un tableau

Pour déclarer un tableau, on définit un type comme pour une variable de base, auquel on ajoute des crochets ouvrant et fermant pour indiquer qu’il s’agit d’un tableau.

Création d’un tableau de types primitifs

Pour créer un tableau dont on connaît sa taille, il est possible de le préciser au moment de sa déclarations.

Pour un tableau devant contenir 10 entiers, vous pourrez utiliser la syntaxe suivante :

  1. int[] t = new int[ 10 ];

Dans le cas d’un programme dont la taille du tableau n’est connue qu’à l’exécution, vous pouvez déclarer et initialiser la taille du tableau en 2 temps comme suit :

  1. int[] t; // déclaration d'un tableau d'entiers
  2. int nbElements;
  3. ...
  4. t = new int[ nbElements ]; // création du tableau avec la taille contenue dans une variable

Télécharger

Création d’un tableau d’objets

Pour créer un tableau de String, la syntaxe est identique :

  1. String[] noms = new String[ 10 ];

Imaginez que vous ayez créé une classe Personne, vous allez pouvoir créer un tableau de 10 personnes comme suit :

  1. Personne[] personnes = new Personnes[ 10 ];

Remarque : Lors de la déclaration d’un tableau, Java autorise une syntaxe de type C mais cette dernière est déconseillée car considérée comme moins clair, ce qui donne :String s[] ; pour un tableau de chaînes.

 Connaître la taille d’un tableau

Pour connaître la taille d’un tableau, ces derniers disposent d’un attribut publique length qu’il suffit de lire.

Exemple pour connaître la taille d’un tableau t :

  1. int nbElements = t.length;

Remarque : L’attribut length est final c’est à dire non modifiable, ce qui est logique puisqu’un tableau ne peut être redimensionné une fois créé.

 Ecrire et lire le contenu du tableau

Les instructions précédentes ont créées des tableaux de 10 cellules mais ces dernières ne contiennent aucune valeur de définies.

Remarque : En fait, les cellules non initialisées contiennent une valeur par défaut qui dépend du type. Une cellule d’entier aura la valeur zero et une cellule devant contenir un objet aura la valeur null par exemple.

On accède en lecture et en écriture à un tableau en spécifiant le n° de la cellule entre crochets avec un indice allant de 0 à la (taille - 1) du tableau.

Affecter une valeur à une cellule

Affecter une valeur à la première cellule d’un tableau d’entiers :

  1. t[ 0 ] = 2;

Pour affecter un objet dans un tableau, il faut utiliser l’opérateur new avec un constructeur si l’objet n’a pas été créé auparavant :

  1. personnes[ 0 ] = new Personne( "Proust", "Marcel" );

ou en 2 temps :

  1. p = new Personne( "Proust", "Marcel" );
  2. personnes[ 0 ] = p;

Télécharger

Initialiser le contenu d’un tableau lors de sa création

Lorsque l’on connaît les valeurs d’un tableau au moment de sa déclaration, nous n’avons pas besoin de spécifier sa taille et on peut utiliser la syntaxe suivante :

  1. int[] ti = { 1, 2, 3, 4, 5 };
  2. String[] ts = { "titi", "toto" };

Télécharger

On peut également créer un tableau anonyme avec la syntaxe suivante :

  1. new int[] { 1,2,3,4,5 };

Ces 2 syntaxes sont utilisables avec des tableaux d’objets en n’oubliant pas d’appeler les constructeurs pour chaque valeur.

  1. Personne[] ps = { new Personne( "Proust", "Marcel" ), new Personne( "Bowie", "David" ) };

Lire le contenu d’une cellule

Pour lire le contenu d’une cellule, il suffit de connaître son indice.

Lire la 3ième cellule d’un tableau d’entier par exemple :

  1. int entier = t[ 2 ];

Pour un objet :

  1. Personne p = personnes[ 2 ];

 Balayer tous les éléments d’un tableau

On aura souvent besoin d’accéder séquentiellement aux cellules d’un tableau soit pour l’initialiser, soit pour le lire.

A l’aide d’une boucle for

L’exemple suivant montre comment initialiser l’ensemble d’un tableau à l’aide d’une boucle for :

  1. int[] t = new int[ 10 ];
  2.                
  3. for ( int i = 0; i < t.length; i++ )
  4. {
  5.   t[ i ] = i + 1;
  6. }

Télécharger

A l’aide d’une boucle for each

Lorsque dans la boucle, on n’a pas besoin de connaître l’indice de chaque élément à traiter, on peut utiliser la syntaxe de type for each (depuis Java 5).

L’exemple suivant montre comment afficher à l’écran l’ensemble des éléments du tableau d’entiers :

  1. for ( int i: t )
  2. {
  3.   System.out.print( " " + i );
  4. }

Télécharger

L’avantage de cette écriture, est que nous n’avons pas besoin de spécifier le nombre d’éléments du tableau, ce qui peut éviter des erreurs.

A l’aide d’un stream et d’un lambda

Depuis Java 8, nous pouvons utiliser les tableaux en tant que streams. On peut donc écrire l’équivalent de l’exemple précédent comme ceci :

  1. Arrays.stream( t ).forEach( i -> { System.out.print( " " + i ); } );

Une fois un tableau transformé en stream, il est possible de faire des opérations sur l’ensemble comme, par exemple, le calcul de la moyenne d’un tableau de chiffres. Exemple :

  1. int[] t =
  2. ...
  3. double moyenne = Arrays.stream( t ).average().getAsDouble();

Télécharger

Remarque : Voir paragraphe suivant pour plus d’informations sur la classe Arrays utilisée dans cet l’exemple.

 La classe utilitaire Arrays

La classe Arrays (avec un s à ne pas confondre avec le type Array) fournit plusieurs méthodes statiques pour réaliser des actions sur des tableaux comme la recherche, le tris etc… Plusieurs de ces méthodes seront utilisées dans les exemples qui vont suivre.

 Traduire un tableau en chaîne de caractères

Il peut être utile de transformer un tableau en une chaîne de caractères, par exemple pour afficher son contenu dans une console pour lors de tests. On pourrait faire cela à partir d’une boucle mais voyons comment obtenir ce résultat à partir d’une seule instruction.

Si nous initialisons le tableau suivant :

  1. String[] t = { "toto", "tutu", "titi", "tata" };

et si nous écrivons le code suivant :

  1. System.out.println( t );

A l’execution, nous obtenons l’affichage suivant qui n’est pas satisfaisant.

Pour afficher le contenu du tableau, nous allons utiliser une méthode de la calsse Arrays.

  1. System.out.println( Arrays.toString( t ) );

Ce qui donne :

ATTENTION : La méthode Arrays.toString() utilise la méthode toString() de chaque élément lorsqu’il s’agit d’objets. Vous devrez donc ne pas oublier de la redéfinir dans vos propres classes.

Si le type d’affichage proposé par défaut ne nous conviens pas, depuis Java 8 nous pouvons utiliser la méthode String.join() qui nous permet de choisir le séparateur comme dans l’exemple suivant :

  1. System.out.println( String.join( ".", t ) );

Ce qui donne :

Cas particulier des tableaux de char

Pour les tableaux de char, il est possible d’afficher le contenu directement :

  1. char[] tc = { 'A', 'B', 'C' };
  2. System.out.println( tc );

Télécharger

qui affiche :

 Copie de tableaux

Utilisation de l’opérateur égal avec les tableaux

Comme les tableaux sont des objets créés sur le tas (opérateur new), si vous utilisez l’opérateur =, vous obtiendrez 2 références sur un même tableau.

Prenons l’exemple suivant :

  1. int[ ] t1 = { 1,2,3,4,5 };     
  2. int[ ] t2 = t1;  // t2 pointe sur le même tableau que t1              
  3. t2[ 0 ] = 100; // on affecte 100 à la première cellule de t2
  4. System.out.println( t1[ 0 ] ); // affiche 100. La première cellule de t1 contient également 100

Télécharger

t1[ 0 ] aura la même valeur que t2[ 0 ] car les 2 variables font références au même tableau en mémoire du fait de l’utilisation de l’opérateur égal.

Utilisation de la méthode clone()

Les tableaux sont clonable et redéfinissent la méthode clone() que l’on peut donc utiliser pour créer une copie de tableau.

Reprenons le cas d’un tableau d’entiers.

  1. int[] t1 = { 1,2,3,4,5 };      
  2. int[] t2 = t1.clone();  // t2 pointe sur un nouveau tableau copie de t1        
  3. t2[ 0 ] = 100; // on affecte 100 à la première cellule de t2                         
  4. System.out.println( "\n" + t1[ 0 ] ); // affiche toujours 1. t1 n'est pas modifié

Télécharger

L’exemple précédent montre bien que nous avons à faire à 2 tableaux différents.

Remarque : Avant Java 5, vous auriez été obligé de transtyper le tableau retourné par la méthode clone().

Cas des tableaux d’objets

Nous pourrons également utiliser la méthode clone() pour les tableaux d’objets. Il faudra simplement se souvenir que chaque cellule du second tableau contiendra une référence sur le même objet que la cellule correspondante du premier tableau (pas de copie profonde). Donc, si vous réinitialisez un nouvel objet dans la cellule du second tableau, la cellule correspondante du premier tableau ne sera pas modifiée et il n’y aura pas d’effet de bord, par contre, si vous modifiez les propriétés d’un objet à partir d’un tableau venant d’être copié, vous modifierez également l’objet référencé par le second tableau.

L’exemple suivant crée un premier tableau avec 2 objets modifiables. On copie ce premier tableau dans un second à l’aide de clone() , puis on modifie un des objet contenu dans la cellule du premier tableau.

  1. Personne[] t1 = { new Personne( "Proust", "Marcel", 1000.00 ) ,
  2.                      new Personne( "Bowie",  "David",  2000.00 ) };
  3.  
  4. Personne[] t2 = t1.clone(); // copie du tableau
  5.  
  6. t1[ 0 ].setNom( "Dupont" ); // on modifie un attribut de l'objet référencé dans t1
  7.                
  8. System.out.println( t1[ 0 ].getNom() ); // affiche "Dupont" à partir de t1
  9. System.out.println( t2[ 0 ].getNom() ); // affiche "Dupont" à partir de t2

Télécharger

Que l’on accède à partir du premier ou du second tableau, c’est un même objet qui est modifié. Les tableaux sont bien différents et leurs cellules sont bien dupliquées mais chacune contient une valeur identique qui est la référence sur un même objet.

Remarque : Si vous utilisez des tableaux d’objets immuables comme des String ou LocalDate, vous n’aurez pas ce risque puisque par définition, ce sont de nouveaux objets qui sont créés en cas de modification.

Remarque : Les tableaux peuvent être dupliqués même lorsqu’ils contiennent des objet non clonable du fait que la duplication n’effectue pas de copie profonde. Dans le cas d’objets, il n’y a que les références qui sont dupliquées.

 Copie d’une portion de tableau

Pour ne copier qu’un groupe d’éléments d’un tableau dans un autre, Java fournit la méthode suivante :

System.ArrayCopy( tableauSource, posDepart, tableauDestination, posDepart, nbElementsACopier );

Exemple :

  1. int[] t1 = { 1, 2 ,3 ,4 ,5, 6, 7, 8, 9, 10 };
  2. int[] t2 = { -4, -5, -6, -7, 0, 0 };           
  3.  
  4. System.arraycopy( t2, 0, t1, 3, 4 );
  5.                
  6. System.out.println( Arrays.toString( t1 ) );

Télécharger

Affiche :

Cette méthode est très pratique car elle permet d’utiliser le même tableau comme source et destination pour, par exemple décaler les valeurs de cellules.

L’exemple suivant décale les valeur de 1 vers la gauche comme si l’on supprimait la première cellule :

  1. int[] t1 = { 1, 2 ,3 ,4 ,5, 6, 7, 8, 9, 10 };
  2.                
  3. System.arraycopy( t1, 1, t1, 0, 9 );
  4.                
  5. System.out.println( Arrays.toString( t1 ) );

Télécharger

Affiche :

 Initialiser un tableau à partir d’une String

Il est possible d’obtenir un tableau de char à partir d’une chaine de caractère en utilisant la méthode toCharArray()

Exemple :

  1. char[] t = s.toCharArray();

 Remplir un tableau avec une valeur par défaut.

Arrays.fill( tableau, valeur );

On peut également ne remplir qu’une partie du tableau en indiquant le premier indice de cellule et le dernier non compris.

Arrays.fill( tableau, indiceDebut, indiceFin, valeur );

Exemple :

  1. int[] t = new int[ 10 ];
  2. System.out.println( Arrays.toString( t ) );
  3. //---          
  4. Arrays.fill( t, 10 );
  5. System.out.println( Arrays.toString( t ) );
  6. //---          
  7. Arrays.fill( t, 2, 4, -1 );
  8. System.out.println( Arrays.toString( t ) );

Télécharger

Affiche :

 Traduire un tableau en liste

Dans certains cas, on peut avoir besoin d’utiliser des méthodes qui manipulent des collections alors que nos données se trouvent dans un tableau. Pour utiliser ce dernier comme une liste, on peut utiliser la méthode suivante :

Arrays.asList( tableau )

ATTENTION : Cette méthode ne duplique pas les données dans une liste, elle permet simplement d’utiliser le tableau comme une liste.

Exemple :

  1. String[] choix = { "oui", "non" };             
  2. List< String > uneListe = Arrays.asList( choix );              
  3. uneListe.stream().forEach( System.out::println );

Télécharger

 Trier un tableau

La classe Arrays fournit plusieurs méthodes de tris permettant de trier différents types de données basées sur l’algorithme QuickSort.

Arrays.sort( tableau ).

Trier un tableau de types primitifs

Pour trier des tableaux de types primitifs, vous pourrez utiliser les méthodes correspondantes :

Arrays.sort( byte[] t );
Arrays.sort( char[] t );
etc...

Exemple de tri d’un tableau d’entiers :

  1. int[] t = { 56, 96, 4, 0, 1, 2098, 2, 5, 1, 69, 26, 95, -26, 654, 1, 3, -1 };
  2. Arrays.sort( t );
  3. System.out.println( Arrays.toString(  t ) );

Télécharger

Affiche :

Trier un tableau d’objets

Les principales méthodes, pour trier des tableaux d’objets sont :

Arrays.sort( Object[] t );
Arrays.sort T[], Comparator< ? super T > c );

Votre tableau devra contenir des objets implémentant des l’interface Comparable si vous utilisez la première méthode sinon, vous devrez utiliser la seconde méthode avec un objet Comparator.

L’exemple suivant montre une tentative de tris avec un tableau d’objets non comparables :

  1. Personne[] t1 = { new Personne( "Proust", "Marcel", 1000.00 ) ,
  2.                   new Personne( "Bowie",  "David",  2000.00 ) };
  3. Arrays.sort( t1 );

Télécharger

qui affichera l’erreur suivante :

Si nous désirons trier le tableau de personnes d’après leurs noms, nous pourrons utiliser la seconde méthode en définissant un Comparator comme dans l’exemple suivant :

  1. Personne[] t1 = { new Personne( "Proust", "Marcel", 1000.00 ) ,
  2.                   new Personne( "Bowie",  "David",  2000.00 ) };
  3. //             
  4. Arrays.sort( t1, new Comparator<Personne >() {
  5.                    @Override
  6.                    public int compare( Personne o1, Personne o2 )
  7.                    {
  8.                       return o1.getNom().compareTo( o2.getNom() );
  9.                    }
  10.                 } );

Télécharger

En Java 8 on peut écrire l’appel à la méthode de tri de manière plus concise avec un lambda. Ce qui donne :

  1. Arrays.sort( t1, ( p1, p2 ) -> p1.getNom().compareTo( p2.getNom() ) );

 Recherche dans un tableau

La méthode de recherche d’un élément dans un tableau est :

Arrays.binarySearch( tableau, recherche ): int

Si la valeur est trouvée, la méthode renvoi son indice dans le tableau, sinon renvoi une valeur négative.

ATTENTION : Le tableau doit être trié.

Exemple :

  1. int[] t = { 56, 12, 9, 2, 4, 6, 1, 0, 297, 100, -56 };
  2. //---                  
  3. int pos = Arrays.binarySearch( t, 4 );
  4. System.out.println( "pos= " + pos );
  5. //---  
  6. Arrays.sort( t );
  7. System.out.println( "tableau trié: " + Arrays.toString( t ) );
  8. //---
  9. pos = Arrays.binarySearch( t, 4 );
  10. System.out.println( "pos= " + pos );

Télécharger

Affiche :

En ce qui concerne la recherche dans un tableau d’objets, ce dernier devra être trié donc les objets devront être comparables ou alors, utiliser la méthode suivante en spécifiant le Comparator.

Arrays.binarySearch( T[], T clé, Comparator< ? super T > c );

 Les tableaux en paramètres et en retour de méthodes

Passage de paramètres

Les tableaux peuvent être passés en paramètres de méthodes mais vous ne pouvez pas définir une taille lors de la déclaration de la méthode. Vous devrez donc ajouter du code à l’intérieur de la méthode pour tester le nombre d’éléments du tableau.

Exemple :

  1. public static void methodeAvecParamTableau( int[] t )
  2. {
  3.     int nbElements = t.length;
  4.     ...

Télécharger

L’exemple précédent peut être remplacée par une méthode avec un nombre de paramètres variables puisque ces derniers se retrouvent dans un tableau dans la méthode.

Exemple :

  1. public static void methodeAvecNbParamsVariables( int... valeurs )
  2. {
  3.     int nbParams = valeurs.length;
  4.     ...        

Télécharger

Remarque : Même avec la seconde syntaxe, il sera possible d’appeler la méthode en lui passant un tableau sous la forme d’un tableau anonyme.

Tableau en retour d’une méthode

Une méthode peut très bien renvoyer un tableau qui pourra être d’une taille quelconque.

  1. public static String[] getTableau()
  2. {
  3.     String[] result = new String[ 10 ];
  4.     //… initialisation du tableau.           
  5.     return result;
  6. }

Télécharger

Remarques générales sur les tableaux et méthodes

Les tableaux étant des objets, que ce soit en tant que paramètres ou de valeurs de retour d’une méthode, il s’agit bien de références qui sont utilisées et les règles et comportements sont les mêmes que pour les instances de classes.

 Test d’égalité de 2 tableaux

Les tableaux étant des objets, ils héritent bien d’une méthode equals() mais qui se contente, comme l’opérateur == de ne tester que les références et non pas le contenu du tableau. Il faudra donc utiliser la méthode Arrays.equals() pour tester si 2 tableaux différents sont égaux.

Exemple :

  1. int[] t1 = { 1, 2, 3 };
  2. int[] t2 = { 1, 2, 3 };
  3. int[] t3 = t1;
  4. //             
  5. if ( !t1.equals( t2 ) ) System.out.println( "t1 et t2 sont pas les mêmes" );
  6. //             
  7. if ( Arrays.equals( t1, t2) ) System.out.println( "t1 et t2 sont égaux" );
  8. //             
  9. if ( t1.equals( t3 ) ) System.out.println( "t1 et t3 sont les mêmes" );

Télécharger

affiche :

 Conversion de types de tableaux

Les types Tableaux héritent de Object et implémentent les interfaces Cloneable et Serializable donc, tout tableau peut être converti dans un de ces trois types.

  1. String[] tableauString = { "Durand", "Proust", "Balsac", "Hugo", "Modiano", "Dupond" };
  2. //             
  3. Object unObjet = tableauString;
  4. Serializable objetSerializable = tableauString;
  5. Cloneable unCloneable = tableauString;

Télécharger

Dans le cas de tableaux d’objets, vous pourrez également transtyper un type tableau dans un type parent

Par exemple, le type String implémente l’interface Comparable. On peut donc écrire le code suivant :

  1. Comparable[] unTableauComparable = tableauString;

 Conclusion

Nous venons de voir les principales possibilités des tableaux à une dimensions en langage Java.

Dans un autre paragraphe nous traiterons des tableaux à plusieurs dimensions qui ne sont en fait que des tableaux de tableaux mais qui ont quelques méthodes spécifiques pour leurs traitement.


Article n° 94

Crée par: chris

Créé le: 16 août 2017

Modifié le: 13 avril 2018

Nombre de visites: 3802

Popularité: 100 %

Popularité absolue: 6

Mots clés de cet article


SPIP

2003-2019 LePpf
Plan du site | | Contact | RSS 2.0

Visiteurs connectés : 6

Nombre moyen de visites quotidiennes sur le site: 150