FAQ Langage JavaConsultez toutes les FAQ
Nombre d'auteurs : 42, nombre de questions : 297, dernière mise à jour : 19 septembre 2017 Ajouter une question
Cette FAQ a été réalisée à partir des questions fréquemment posées sur le forum Java de http://java.developpez.com ainsi que l'expérience personnelle des auteurs.
Nous tenons à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes. Les auteurs font leur maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous trouvez une erreur, ou que vous souhaitez nous aider en devenant rédacteur, lisez ceci.
Sur ce, nous vous souhaitons une bonne lecture.
- Qu'est-ce qu'un tableau ?
- Comment déclarer un tableau ?
- Comment savoir si un objet est un tableau ?
- Comment accéder aux éléments d'un tableau ?
- Comment connaître la taille d'un tableau ?
- Comment changer la taille d'un tableau ?
- Quelle est la taille maximale d'un tableau ?
- Comment parcourir tous les éléments d'un tableau ?
- Comment créer un tableau à dimensions multiples ?
- Comment déterminer le nombre de dimensions d'un tableau ?
- Comment parcourir un tableau à deux dimensions avec un for étendu ?
- Comment trier le contenu d'un tableau ?
- Comment créer un tableau en utilisant un type générique ?
- Pourquoi ne peut-on pas instancier de tableau paramétré ?
Un tableau (ou array) est un type de stockage de données contigües similaire à ce qu'on peut trouver dans d'autres langages de programmation. Un tableau peut contenir des literals, des références d'objets, y compris des références vers d'autres tableaux. Un tableau contenant des références peut contenir la valeur null, un tableau contenant des literals ne le peut pas.
Attention : en Java, un tableau n'est pas un pointeur adressant un bloc mémoire arbitraire. Ici, il s'agit d'un objet qui hérite implicitement de la classe java.lang.Object et sur lequel il est même possible d'invoquer quelques méthodes (celles définies dans la classe Object) !
Pour déclarer un tableau en Java, vous devez spécifier un type (soit primitif, soit objet) suivi de la notation [] (crochet ouvrant suivi de crochet fermant). Par exemple :
Code Java : | Sélectionner tout |
1 2 3 | int[] tableauEntier; // un tableau d'entiers. String[] tableauChaine; // un tableau de chaînes de caractères. Voiture[] tableauVoiture; // un tableau d'instances de la classe Voiture. |
Les variables ainsi déclarées sont automatiquement du type décrit, ainsi tableauEntier est de type int[], tableauChaine est de type String[] tandis que la variable tableauVoiture est de type Voiture[] même si Voiture est un type que vous avez créé.
Cependant, une telle variable n'est pas initialisée. Dans le cas d'une variable membre d'une classe, elle sera implicitement à la valeur null ; tandis que dans une méthode, le compilateur générera une erreur à cause du défaut d’initialisation. Pour initialiser un tableau, il faut l'allouer, ce qui peut être fait en invoquant l’opérateur new et en donnant une taille au tableau, soit via une constante entière, soit via une variable entière. Par exemple :
Code Java : | Sélectionner tout |
int[] tableauEntier = new int[10]; // Le tableau contiendra 10 éléments.
Attention cependant, une telle initialisation mettra le contenu du tableau à des valeurs par défaut :
- boolean - le contenu du tableau sera initialisé à la valeur false ;
- Nombres primitifs - le contenu du tableau sera initialisé à la valeur 0, 0F, 0L ou 0D suivant le type utilisé ;
- Objets (y compris String, les classes wrapper des nombres et des sous-tableaux) - le contenu du tableau sera initialisé à la valeur null.
Il est également possible d'initialiser un tableau en donnant directement son contenu par des constantes ou des variables. Par exemple :
Code Java : | Sélectionner tout |
int[] tableauEntier = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // Le tableau contiendra 10 éléments.
Ou :
Code Java : | Sélectionner tout |
int[] tableauEntier = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // Le tableau contiendra 10 éléments.
Pour savoir si un objet est un tableau, il suffit de faire :
Code Java : | Sélectionner tout |
boolean estUnTableau = monObjet.getClass().isArray();
Pour accéder aux éléments d'un tableau, vous devez utiliser la syntaxe <nom du tableau>[<index de la valeur>] où l'index de la valeur est un entier positif ou strictement inférieur à la taille du tableau. Cette valeur peut être une constante ou le contenu d'une variable. L'objet retourné est du même type que le contenu du tableau.
De plus, en Java, la première cellule du tableau est toujours à l'index 0. Ainsi, un tableau de taille N sera indexable par la plage d'indices [0, N-1].
Par exemple :
Code Java : | Sélectionner tout |
1 2 3 | int[] tableauEntier = {1 , 2 , 3, 4, 5, 6, 7, 8, 9, 10}; int valeur = tableauEntier[2]; // Récupère le 3e élément du tableau (index 2). tableauEntier[0] = 5; // Modifie la valeur du 1er élément du tableau (index 0). |
Note : un tableau vide (initialisé avec la taille 0) ne contient aucun élément.
Avertissement : en Java, les accès aux données d'un tableau sont sécurisés. Si jamais vous tentez d’accéder au contenu du tableau en utilisant un index négatif ou supérieur ou égal à la taille du tableau, votre code générera une exception de type java.lang.ArrayIndexOutOfBoundsException.
Chaque tableau, qu'il contienne des primitifs ou des références vers des objets dispose d'un membre length qui contient la taille du tableau sous forme d'une valeur entière positive ou nulle. Ce membre a le modificateur final et n'est donc pas modifiable.
Ainsi, il est possible de faire :
Code Java : | Sélectionner tout |
1 2 | int[] tableau = {1, 2, 3}; System.out.println(tableau.length); // Imprime 3 sur la console. |
Il est impossible de changer la taille d'un tableau : la taille d'un tableau est fixe et ne peut pas être modifiée une fois que le tableau a été instancié.
Vous devez donc allouer un nouveau tableau à la taille appropriée et recopier le contenu de l'ancien tableau dans le nouveau. La méthode publique statique arraycopy() de la classe java.lang.System peut être utilisée à cet effet. Par exemple :
Code Java : | Sélectionner tout |
1 2 3 4 5 | public int[] doubleLaTaille(final int[] source) { final int[] result = new int[source.length * 2]; System.arraycopy(source, 0, result, 0, source.length); // Recopie le contenu de la source au début du résultat. return result; } |
La création d'un nouveau tableau est une opération coûteuse en termes de performances, si vous devez utiliser une structure de données dont la taille change souvent, il est fortement conseillé d'utiliser une liste.
Dans les spécifications du langage Java, il est indiqué que, pour indexer un élément dans un tableau, il faut utiliser une valeur entière positive ou nulle.
En théorie, cela implique donc que le tableau contient au maximum 231-1 éléments soit la valeur Integer.MAX_VALUE, le plus grand entier positif possible. La plus grande plage d'indices possible pour un tableau est donc [0, 231-2]. Cependant, il s'agit d'une limite théorique.
En pratique, dans les JVM récentes, il semble qu'il ne soit pas possible d'allouer un tableau dont la taille dépasse Integer.MAX_VALUE - 2. Toute tentative d'allouer une taille supérieure à cette limite se soldera par une erreur de type java.lang.OutOfMemoryError contenant le message « Requested array size exceeds VM limit ». Même si la valeur est dans la limite supportée, une erreur java.lang.OutOfMemoryError contenant le message « Java heap space » peut être générée si la JVM échoue à allouer assez de mémoire pour créer un tel tableau en fonction du type des données stockées dans le tableau.
Code Java : | Sélectionner tout |
1 2 3 4 5 6 7 8 | // Un tableau de N bytes consomme N octets en mémoire. final byte[] test = new byte[Integer.MAX_VALUE]; // Échoue avec java.lang.OutOfMemoryError: Requested array size exceeds VM limit final byte[] test = new byte[Integer.MAX_VALUE-1]; // Échoue avec java.lang.OutOfMemoryError: Requested array size exceeds VM limit final byte[] test = new byte[Integer.MAX_VALUE-2]; // Fonctionne, // Un tableau de N int consomme 4 * N octets en mémoire. final int[] test = new int[Integer.MAX_VALUE]; // Échoue avec java.lang.OutOfMemoryError: Requested array size exceeds VM limit final int[] test = new int[Integer.MAX_VALUE-1]; // Échoue avec java.lang.OutOfMemoryError: Requested array size exceeds VM limit final int[] test = new int[Integer.MAX_VALUE-2]; // Échoue avec java.lang.OutOfMemoryError: Java heap space |
Il existe de très nombreuses manières de parcourir tous les éléments d'un tableau. Nous allons en aborder quelques-unes des plus courantes :
Boucle for ou while
Nous pouvons effectuer le parcours à l'ancienne, avec une boucle for traditionnelle, par exemple :
Code Java : | Sélectionner tout |
1 2 3 4 5 | int[] tab = new int[50] [...] for (int index = 0; index < tab.length; index++) { System.out.println(tab[index]); } |
Ou une boucle while :
Code Java : | Sélectionner tout |
1 2 3 4 5 6 7 | int[] tab = new int[50] [...] int index = 0; while (index < tab.length) { System.out.println(tab[index]); index++; } |
Boucle for étendu
Le JDK 5.0 apporte une construction de boucle performante qui permet de parcourir tous les éléments d'un tableau, ou d'un objet implémentant l'interface Iterable (ArrayList, etc.), sans se préoccuper de l'indice.
Code Java : | Sélectionner tout |
1 2 3 4 5 | int[] tab = new int[50] [...] for (int n : tab) { System.out.println(n); } |
Streams
Le JDK 8 permet d'utiliser des flux sur des tableaux. Par exemple pour itérer sur l'index des valeurs :
Code Java : | Sélectionner tout |
1 2 3 4 | int[] tab = new int[50] [...] IntStream.range(0, tab.length) .forEach(index -> System.out.println(tab[index])); |
Ou encore pour itérer sur les valeurs elles-mêmes :
Code Java : | Sélectionner tout |
1 2 3 4 | int[] tab = new int[50]; [...] Arrays.stream(tab) .forEach(System.out::println); |
Java ne supporte pas les tableaux multidimensionnels en tant que bloc mémoire unifié comme peuvent le faire d'autres langages. Il reste cependant possible de créer des tableaux de tableaux qui peuvent jouer un rôle similaire pour simuler des tableaux à dimensions multiples.
Pour indiquer que votre variable contient plus d'une seule dimension, il suffit de rajouter dans sa déclaration de type autant de [] qu'il y a de dimensions supplémentaires. Par exemple :
Code Java : | Sélectionner tout |
1 2 | int[][] matriceEntiere2D; String[][][] matriceChaine3D; |
En ce qui concerne l'initialisation, il est possible d'initialiser les dimensions séparément en commençant par les dimensions les plus à gauche :
Code Java : | Sélectionner tout |
1 2 | int[][] matriceEntiere2D = new int[10][]; int[][] matriceEntiere2D = new int[][10]; // Erreur. |
Ici, les sous-tableaux sont à la valeur null, car ils n'ont pas été alloués. Par exemple :
Code Java : | Sélectionner tout |
1 2 | System.out.println(matriceEntiere2D[5])); // Imprime null. System.out.println(matriceEntiere2D[5][3])); // génère une NullPointerException. |
Il faudra donc songer à les initialiser :
Code Java : | Sélectionner tout |
1 2 3 4 | int[][] matriceEntiere2D = new int[10][]; for (int i = 0 ; i < 10 ; i++) { matriceEntiere2D[i] = new int[10]; // Initialisation des sous-tableaux. } |
Il est également possible d'initialiser toutes les dimensions en une seule fois.
Code Java : | Sélectionner tout |
int[][] matriceEntiere2D = new int[10][10];
Dans ce cas tous les sous-tableaux sont automatiquement alloués et ils seront tous de la même taille.
Dans les deux cas, les valeurs contenues dans chaque sous-tableau seront alors initialisées avec les valeurs par défaut habituelles (ici 0 puisque que nous manipulons des int).
Il est également possible d'initialiser directement le contenu des tableaux avec des constantes ou des variables. Par exemple :
Code Java : | Sélectionner tout |
String[][] matriceChaine2D = {{"Foo", "Fii"}, {"Faa", "Fuu"}};
Avertissement : gardez à l'esprit que, quand vous manipulez des tableaux de tableaux de ... de tableaux, rien de garantit que tous les sous-tableaux ont été correctement initialisés ou même qu'ils ont tous la même taille.
Pour accéder au contenu d'un tableau de tableaux, il suffit de rajouter autant de [<index de la valeur>] qu'il y a de dimensions supplémentaires et d'utiliser des valeurs d'indices légales pour la dimension de chaque sous-tableau qui est accédé. Il est également possible d’accéder ainsi aux divers sous-tableaux. Par exemple :
Code Java : | Sélectionner tout |
1 2 3 4 5 | int[][] matriceEntier2D = new int[10][10]; int valeur = matriceEntier2D[5][7]; // Vaut 0. int[] vecteur = matriceEntier2D[8]; // Vaut [0,0,0,0,0,0,0,0,0,0]. matriceEntier2D[2][1] = 8; matriceEntier2D[1][6] = null; |
Cette propriété n'est pas directement accessible, mais la fonction suivante permet de la calculer rapidement. On se base sur le fait qu'un tableau à plusieurs dimensions est en fait un tableau de tableaux.
Code Java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | public static int getNbDimension(Object monTableau) { int dim=0; Class cls = monTableau.getClass(); while( cls.isArray()) { cls = cls.getComponentType(); dim++; } return( dim ); } |
Le code suivant permet de parcourir un tableau à deux dimensions d'entiers et de les afficher :
Code Java : | Sélectionner tout |
1 2 3 4 5 6 | int tab[][] = { {1, 2, 3}, {7, 8, 9} }; for(int ligne[] : tab) { for(int element : ligne) { System.out.println("Item : " + element); } } |
Pour trier le contenu d'un tableau, vous devez invoquer une des variantes de la méthode sort() de la classe utilitaire java.util.Arrays :
Code Java : | Sélectionner tout |
1 2 3 4 5 6 7 8 | int[] unTableauDEntiers = [...] Arrays.sort(unTableauDEntiers); float[] unTableauDeFlottants= [...] Arrays.sort(unTableauDeFlottants); Voiture[] unTableauDeVoitures = [...] Arrays.sort(unTableauDeVoitures); |
Par défaut, c'est l'ordre naturel des objets qui sera utilisé. Il est cependant possible de fournir un Comparator qui se chargera de faire le tri. Prenons, par exemple, un tableau contenant des voitures. Nous allons trier celui-ci en fonction de la puissance des véhicules :
Code Java : | Sélectionner tout |
1 2 | Comparator<Voiture> comparateurDePuissance = [...] // Compare le nombre de chevaux au lieu du nom de la voiture. Arrays.sort(unTableauDeVoitures, comparateurDePuissance ); |
La méthode sort() effectue un tri rapide par double-pivot d’après l'algorithme de Vladimir Yaroslavskiy, Jon Bentley, et Joshua Bloch.
Depuis le JDK 8, il est également possible d'invoquer une des variantes de la méthode parallelSort() de la classe java.util.Arrays qui exécutera le tri en parallèle sur des sous-sections du tableau en utilisant plusieurs threads.
Contrairement à ce qu'on pourrait penser, il est possible de créer des instances de tableaux génériques. Il suffit pour cela d'invoquer une des variantes de la méthode publique statique newInstance() de la classe java.lang.reflect.Array. La première version de la méthode permet de créer un tableau classique à une dimension, tandis que la seconde version permet de spécifier de multiples dimensions. Dans les deux cas, il faut cependant détenir une référence vers la classe que nous désirons stocker dans le tableau.
Ainsi il est possible de faire :
Code Java : | Sélectionner tout |
String[] tableau = (String[])Array.newInstance(String.class, 100);
Ou dans un environnement qui manipule des génériques sans connaitre la valeur réelle du type T :
Code Java : | Sélectionner tout |
T[] tableau = (T[])Array.newInstance(uneValeurDeTypeT.getClass(), 100);
Le compilateur interdit toute création de tableaux de type paramétré, ainsi la ligne suivante provoquera irrémédiablement une erreur :
Code java : | Sélectionner tout |
List<String> stringListArray[] = new List<String>[100];
Ce problème vient du fait que les tableaux et les Generics ont une approche totalement opposée de la vérification des types.
Avec les tableaux, la quasi-totalité des vérifications sont effectuées pendant l'exécution, et la plupart des opérations sur les tableaux peuvent générer des ClassCastException selon le type réel du tableau et le type réel de l'élément qu'on lui affecte.
À l'inverse, les Generics sont sécurisés à la compilation. C'est-à-dire que c'est le compilateur qui se charge de vérifier la cohérence de l'ensemble pendant la compilation, et qui garantit l'absence d'exception pendant l'exécution.
Du fait de leurs fortes oppositions et de l'héritage particulier des tableaux, l'utilisation de tableaux Generics peut aboutir à des situations complètement fausses et générer des exceptions inattendues.
Ainsi, si la création de tableaux Generics était possible, il serait relativement simple de passer outre la protection des Generics sans warning ni erreur :
Code java : | Sélectionner tout |
1 2 3 4 5 | List<String>[] stringListArray = new List<String>[100]; // ERREUR Object[] simpleArray = stringListArray; simpleArray[0] = new ArrayList<Number>(); // OK ?!?!? |
Le pire c'est que ce code ne générerait même pas d'exception lors de son exécution. Au contraire l'exécution serait reportée lors de l'utilisation des données du tableau alors qu'il devrait s'agir d'un code sécurisé...
Bref, l'utilisation de tableaux Generics est loin d'assurer la sécurité de code promise par les Generics, et le compilateur interdit donc leur création.
Il existe pourtant une alternative.
La meilleure solution serait d'utiliser l'API de Collection, et donc une List à la place du tableau. En effet dans le cas de figure décrit ci-dessus, l'utilisation des Generics permettrait d'être averti du problème via un warning :
Code java : | Sélectionner tout |
1 2 3 4 5 | List<List<String>> stringListList = new ArrayList<List<String>>(); List simpleList = stringListList; simpleList.add( new ArrayList<Number>() ); // Warning unchecked |
La seconde solution, à n'utiliser que si l'utilisation du tableau est vraiment une nécessité, consiste à créer un tableau non Generics et à ignorer le warning reçu :
Code java : | Sélectionner tout |
1 2 | @SuppressWarnings("unchecked") List<String>[] stringListArray = new List[100]; |
Mais bien entendu cela ne corrige en rien le problème des tableaux Generics…
Proposer une nouvelle réponse sur la FAQ
Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour çaLes sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.