 |
 |
9) Stockage des objets |
|
 |
|
Texte original |
 |
Traducteur : Jérome QUELIN |
|
 |
|
 |
 |
 |
 |
 |
 |
|
 |
05.07.01 - version 5.4 [Armel] : - Ajout des tags de mise en page pour la verion du site. 28.04.2001 - version 5.3 : - Mise en forme html. 16.07.2000 - version 5.2 - Corrections apportées par Jean-Pierre Vidal. 14.07.2000 - version 5.1 - Première publication sur eGroups. Traducteur : - Jérome QUELIN Texte original : -Thinking in Java, 2nd edition, Revision 10 © 2000 by Bruce Eckel
|
 |
 |
 |
9: Holding Your Objects
|
 |
9 : Stockage des objets
|
 |
 |
 |
It’s a fairly simple program
that has only a fixed quantity of objects with known lifetimes.
|
 |
C'est un programme relativement simple que celui qui ne
manipule que des objets dont le nombre et la durée de vie sont connus à l'avance.
|
 |
 |
 |
In general, your programs will always be
creating new objects based on some criteria that will be known only at the time
the program is running. You won’t know until run-time the quantity or even
the exact type of the objects you need. To solve the general programming
problem, you need to be able to create any number of objects, anytime, anywhere.
So you can’t rely on creating a named reference to hold each one of your
objects:
|
 |
Mais en général, vos programmes créeront de nouveaux objets basés sur des
informations qui ne seront pas connues avant le lancement du programme. Le nombre voire même le
type des objets nécessaires ne seront pas connus avant la phase d'exécution du programme. Pour
résoudre le problème considéré, il faut donc être capable de créer un certain nombre d'objets,
n'importe quand, n'importe où. Ce qui implique qu'on ne peut se contenter d'une référence nommée
pour stocker chacun des objets du programme :
|
 |
 |
 |
MyObject myReference;
|
 |
MyObject myReference;
|
 |
 |
 |
since you’ll never know how many of these you’ll actually need.
|
 |
puisqu'on ne connaît pas le nombre exact de références qui seront
manipulées.
|
 |
 |
 |
To solve this rather essential problem, Java has several ways to hold objects (or rather, references to objects). The built-in type is the array, which has been discussed before. Also, the Java utilities library has a reasonably complete set of container classes (also known as collection classes, but because the Java 2 libraries use the name Collection to refer to a particular subset of the library, I shall use the more inclusive term “container”). Containers provide sophisticated ways to hold and even manipulate your objects.
|
 |
Pour résoudre ce problème fondamental, Java dispose de plusieurs manières
de stocker les objets (ou plus exactement les références sur les objets). Le type interne est le
tableau, dont nous avons déjà parlé auparavant. De plus, la bibliothèque des utilitaires de Java
propose un ensemble relativement complet de classes conteneurs (aussi connues sous le
nom de classes collections, mais comme les bibliothèques de Java 2 utilisent le nom
Collection pour un sous-ensemble particulier de cette bibliothèque, j'utiliserai
ici le terme plus générique « conteneur »). Les conteneurs fournissent des moyens
sophistiqués pour stocker et même manipuler les objets d'un programme.
|
 |
 |
 |
Arrays
|
 |
Les tableaux
|
 |
 |
 |
Most of the necessary introduction to arrays is in the last section of Chapter 4, which showed how you define and initialize an array. Holding objects is the focus of this chapter, and an array is just one way to hold objects. But there are a number of other ways to hold objects, so what makes an array special?
|
 |
Les tableaux ont déjà été introduits dans la dernière section du
Chapitre 4, qui montrait comment définir et initialiser un tableau. Ce chapitre traite du stockage
des objets, et un tableau n'est ni plus ni moins qu'un moyen de stocker des objets. Mais il existe
de nombreuses autres manières de stocker des objets : qu'est-ce qui rend donc les tableaux si
spécial ?
|
 |
 |
 |
There are two issues that distinguish arrays from other types of containers: efficiency and type. The array is the most efficient way that Java provides to store and randomly access a sequence of objects (actually, object references). The array is a simple linear sequence, which makes element access fast, but you pay for this speed: when you create an array object, its size is fixed and cannot be changed for the lifetime of that array object. You might suggest creating an array of a particular size and then, if you run out of space, creating a new one and moving all the references from the old one to the new one. This is the behavior of the ArrayList class, which will be studied later in this chapter. However, because of the overhead of this size flexibility, an ArrayList is measurably less efficient than an array.
|
 |
Les tableaux se distinguent des autres types de conteneurs sur deux
points : l'efficacité et le type. Un tableau constitue la manière la plus efficace
que propose Java pour stocker et accéder aléatoirement à une séquence d'objets (en fait, de
références sur des objets). Un tableau est une simple séquence linéaire, ce qui rend l'accès aux
éléments extrêmement rapide ; mais cette rapidité se paye : la taille d'un tableau est
fixée lors de sa création et ne peut être plus être changée pendant toute la durée de sa vie. Une
solution est de créer un tableau d'une taille donnée, et, lorsque celui-ci est saturé, en créer un
nouveau et déplacer toutes les références de l'ancien tableau dans le nouveau. C'est précisément ce
que fait la classe ArrayList, qui sera étudiée plus loin dans ce chapitre.
Cependant, du fait du surcoût engendré par la flexibilité apportée au niveau de la taille, une
ArrayList est beaucoup moins efficace qu'un tableau.
|
 |
 |
 |
The vector container class in C++ does know the type of objects it holds, but it has a different drawback when compared with arrays in Java: the C++ vector’s operator[] doesn’t do bounds checking, so you can run past the end[44]. In Java, you get bounds checking regardless of whether you’re using an array or a container—you’ll get a RuntimeException if you exceed the bounds. As you’ll learn in Chapter 10, this type of exception indicates a programmer error, and thus you don’t need to check for it in your code. As an aside, the reason the C++ vector doesn’t check bounds with every access is speed—in Java you have the constant performance overhead of bounds checking all the time for both arrays and containers.
|
 |
La classe conteneur vector en C++ connaît le
type des objets qu'il stocke, mais il a un inconvénient comparé aux tableaux de Java :
l'opérateur [] des vector C++ ne réalise pas de contrôle sur les
indices, on peut donc tenter d'accéder à un élément au-delà de la taille du
vector [44]. En
Java, un contrôle d'indices est automatiquement effectué, qu'on utilise un tableau ou un conteneur
- une exception RuntimeException est générée si les frontières sont
dépassées. Comme vous le verrez dans le Chapitre 10, ce type d'exception indique une erreur due au
programmeur, et comme telle il ne faut pas la prendre en considération dans le code. Bien entendu,
le vector C++ n'effectue pas de vérifications à chaque accès pour des raisons
d'efficacité - en Java, la vérification continuelle des frontières implique une dégradation des
performances pour les tableaux comme pour les conteneurs.
|
 |
 |
 |
The other generic container classes that will be studied in this chapter, List, Set, and Map, all deal with objects as if they had no specific type. That is, they treat them as type Object, the root class of all classes in Java. This works fine from one standpoint: you need to build only one container, and any Java object will go into that container. (Except for primitives—these can be placed in containers as constants using the Java primitive wrapper classes, or as changeable values by wrapping in your own class.) This is the second place where an array is superior to the generic containers: when you create an array, you create it to hold a specific type. This means that you get compile-time type checking to prevent you from putting the wrong type in, or mistaking the type that you’re extracting. Of course, Java will prevent you from sending an inappropriate message to an object, either at compile-time or at run-time. So it’s not much riskier one way or the other, it’s just nicer if the compiler points it out to you, faster at run-time, and there’s less likelihood that the end user will get surprised by an exception.
|
 |
Les autres classes de conteneurs génériques qui seront étudiés dans ce
chapitre, les Lists, les Sets et
les Maps, traitent les objets comme s'ils n'avaient pas de type spécifique.
C'est à dire qu'ils les traitent comme s'ils étaient des Objects, la classe
de base de toutes les classes en Java. Ceci est très intéressant d'un certain point de vue :
un seul conteneur est nécessaire pour stocker tous les objets Java (excepté les types scalaires -
ils peuvent toutefois être stockés dans les conteneurs sous forme de constantes en utilisant les
classes Java d'encapsulation des types primitifs, ou sous forme de valeurs modifiables en les
encapsulant dans des classes personnelles). C'est le deuxième point où les tableaux se distinguent
des conteneurs génériques : lorsqu'un tableau est créé, il faut spécifier le type d'objets
qu'il est destiné à stocker. Ce qui implique qu'on va bénéficier d'un contrôle de type lors de la
phase compilation, nous empêchant de stocker des objets d'un mauvais type ou de se tromper sur le
type de l'objet qu'on extrait. Bien sûr, Java empêchera tout envoi de message inapproprié à un
objet, soit lors de la compilation soit lors de l'exécution du programme. Aucune des deux approches
n'est donc plus risquée que l'autre, mais c'est tout de même mieux si c'est le compilateur qui
signale l'erreur, plus rapide à l'exécution et il y a moins de chances que l'utilisateur final ne
soit surpris par une exception.
|
 |
 |
 |
For efficiency and type checking it’s always worth trying to use an array if you can. However, when you’re trying to solve a more general problem arrays can be too restrictive. After looking at arrays, the rest of this chapter will be devoted to the container classes provided by Java.
|
 |
Du fait de l'efficacité et du contrôle de type, il est toujours préférable
d'utiliser un tableau si c'est possible. Cependant, les tableaux peuvent se révéler trop
restrictifs pour résoudre certains problèmes. Après un examen des tableaux, le reste de ce chapitre
sera consacré aux classes conteneurs proposées par Java.
|
 |
 |
 |
Arrays are first-class objects
|
 |
Les tableaux sont des objets
|
 |
 |
 |
Regardless of what type of array you’re working with, the array identifier is actually a reference to a true object that’s created on the heap. This is the object that holds the references to the other objects, and it can be created either implicitly, as part of the array initialization syntax, or explicitly with a new expression. Part of the array object (in fact, the only field or method you can access) is the read-only length member that tells you how many elements can be stored in that array object. The ‘[]’ syntax is the only other access that you have to the array object.
|
 |
Indépendamment du type de tableau qu'on utilise, un identifiant de tableau
est en fait une référence sur un vrai objet créé dans le segment. C'est l'objet qui stocke les
références sur les autres objets, et il peut être créé soit implicitement grâce à la syntaxe
d'initialisation de tableau, soit explicitement avec une expression new. Une
partie de l'objet tableau (en fait, la seule méthode ou champ auquel on peut accéder) est le membre
en lecture seule length qui indique combien d'éléments peuvent être stockés dans
l'objet. La syntaxe « [] » est le seul autre accès disponible pour
les objets tableaux.
|
 |
 |
 |
The following example shows the various ways that an array can be initialized, and how the array references can be assigned to different array objects. It also shows that arrays of objects and arrays of primitives are almost identical in their use. The only difference is that arrays of objects hold references, while arrays of primitives hold the primitive values directly.
|
 |
L'exemple suivant montre les différentes façons d'initialiser un tableau,
et comment les références sur un tableau peuvent être assignées à différents objets tableau. Il
montre aussi que les tableaux d'objets et les tableaux de scalaires sont quasi identiques
dans leur utilisation. La seule différence est qu'un tableau d'objets stocke des références, alors
qu'un tableau de scalaires stocke les valeurs directement.
|
 |
 |
 |
//: c09:ArraySize.java // Initialization & re-assignment of arrays.
class Weeble {} // A small mythical creature
public class ArraySize { public static void main(String[] args) { // Arrays of objects: Weeble[] a; // Null reference Weeble[] b = new Weeble[5]; // Null references Weeble[] c = new Weeble[4]; for(int i = 0; i < c.length; i++) c[i] = new Weeble(); // Aggregate initialization: Weeble[] d = { new Weeble(), new Weeble(), new Weeble() }; // Dynamic aggregate initialization: a = new Weeble[] { new Weeble(), new Weeble() }; System.out.println("a.length=" + a.length); System.out.println("b.length = " + b.length); // The references inside the array are // automatically initialized to null: for(int i = 0; i < b.length; i++) System.out.println("b[" + i + "]=" + b[i]); System.out.println("c.length = " + c.length); System.out.println("d.length = " + d.length); a = d; System.out.println("a.length = " + a.length);
// Arrays of primitives: int[] e; // Null reference int[] f = new int[5]; int[] g = new int[4]; for(int i = 0; i < g.length; i++) g[i] = i*i; int[] h = { 11, 47, 93 }; // Compile error: variable e not initialized: //!System.out.println("e.length=" + e.length); System.out.println("f.length = " + f.length); // The primitives inside the array are // automatically initialized to zero: for(int i = 0; i < f.length; i++) System.out.println("f[" + i + "]=" + f[i]); System.out.println("g.length = " + g.length); System.out.println("h.length = " + h.length); e = h; System.out.println("e.length = " + e.length); e = new int[] { 1, 2 }; System.out.println("e.length = " + e.length); } } ///:~
|
 |
//: c09:ArraySize.java // Initialisation & ré-assignation des tableaux.
class Weeble {} // Une petite créature mythique
public class ArraySize { public static void main(String[] args) { // Tableaux d'objets : Weeble[] a; // Référence Null. Weeble[] b = new Weeble[5]; // Références Null Weeble[] c = new Weeble[4]; for(int i = 0; i < c.length; i++) c[i] = new Weeble(); // Initialisation par agrégat : Weeble[] d = { new Weeble(), new Weeble(), new Weeble() }; // Initialisation dynamique par agrégat : a = new Weeble[] { new Weeble(), new Weeble() }; System.out.println("a.length=" + a.length); System.out.println("b.length = " + b.length); // Les références à l'intérieur du tableau sont // automatiquement initialisées à null : for(int i = 0; i < b.length; i++) System.out.println("b[" + i + "]=" + b[i]); System.out.println("c.length = " + c.length); System.out.println("d.length = " + d.length); a = d; System.out.println("a.length = " + a.length);
// Tableaux de scalaires : int[] e; // Référence Null int[] f = new int[5]; int[] g = new int[4]; for(int i = 0; i < g.length; i++) g[i] = i*i; int[] h = { 11, 47, 93 }; // Erreur de compilation : variable e non initialisée : //!System.out.println("e.length=" + e.length); System.out.println("f.length = " + f.length); // Les scalaires dans le tableau sont // automatiquement initialisées à zéro : for(int i = 0; i < f.length; i++) System.out.println("f[" + i + "]=" + f[i]); System.out.println("g.length = " + g.length); System.out.println("h.length = " + h.length); e = h; System.out.println("e.length = " + e.length); e = new int[] { 1, 2 }; System.out.println("e.length = " + e.length); } } ///:~
|
 |
 |
 |
Here’s the output from the
program:
|
 |
Voici la sortie du programme :
|
 |
 |
 |
b.length = 5 b[0]=null b[1]=null b[2]=null b[3]=null b[4]=null c.length = 4 d.length = 3 a.length = 3 a.length = 2 f.length = 5 f[0]=0 f[1]=0 f[2]=0 f[3]=0 f[4]=0 g.length = 4 h.length = 3 e.length = 3 e.length = 2
|
 |
a.length = 2 b.length = 5 b[0]=null b[1]=null b[2]=null b[3]=null b[4]=null c.length = 4 d.length = 3 a.length = 3 f.length = 5 f[0]=0 f[1]=0 f[2]=0 f[3]=0 f[4]=0 g.length = 4 h.length = 3 e.length = 3 e.length = 2
|
 |
 |
 |
The array a is initially just a
null reference, and the compiler prevents you from
doing anything with this reference until you’ve properly initialized it.
The array b is initialized to point to an array of Weeble
references, but no actual Weeble objects are ever placed in that array.
However, you can still ask what the size of the array is, since b is
pointing to a legitimate object. This brings up a slight drawback: you
can’t find out how many elements are actually in the array, since
length tells you only how many elements can be placed in the
array; that is, the size of the array object, not the number of elements it
actually holds. However, when an array object is created its references are
automatically initialized to null, so you can see whether a particular
array slot has an object in it by checking to see whether it’s
null. Similarly, an array of primitives is automatically initialized to
zero for numeric types, (char)0 for char, and false for
boolean.
|
 |
Le tableau a n'est initialement qu'une
référence null, et le compilateur interdit d'utiliser cette référence tant
qu'elle n'est pas correctement initialisée. Le tableau b est initialisé afin de
pointer sur un tableau de références Weeble, même si aucun objet
Weeble n'est réellement stocké dans le tableau. Cependant, on peut toujours
s'enquérir de la taille du tableau, puisque b pointe sur un objet valide. Ceci
montre un inconvénient des tableaux : on ne peut savoir combien d'éléments sont actuellement
stockés dans le tableau, puisque length renvoie seulement le nombre
d'éléments qu'on peut stocker dans le tableau, autrement dit la taille de l'objet tableau,
et non le nombre d'éléments qu'il contient réellement. Cependant, quand un objet tableau est créé,
ses références sont automatiquement initialisées à null, on peut donc facilement
savoir si une cellule du tableau contient un objet ou pas en testant si son contenu est
null. De même, un tableau de scalaires est automatiquement initialisé à zéro pour
les types numériques, (char)0 pour les caractères et false pour
les booleans.
|
 |
 |
 |
Array c shows the creation of the
array object followed by the assignment of Weeble objects to all the
slots in the array. Array d shows the “aggregate
initialization” syntax that causes the array object to be created
(implicitly with new on the heap, just like for array c)
and initialized with Weeble objects, all in one
statement.
|
 |
Le tableau c montre la création d'un objet tableau suivi
par l'assignation d'un objet Weeble à chacune des cellules du tableau. Le tableau
d illustre la syntaxe d'« initialisation par agrégat » qui permet de
créer un objet tableau (implicitement sur le segment avec new, comme le tableau
c) et de l'initialiser avec des objets Weeble, le tout
dans une seule instruction.
|
 |
 |
 |
The
next array initialization could be thought of as a “dynamic aggregate
initialization.” The aggregate initialization used by d must be
used at the point of d’s definition, but with the second syntax you
can create and initialize an array object anywhere. For example, suppose
hide( ) is a method that takes an array of Weeble objects.
You could call it by saying:
|
 |
L'initialisation de tableau suivante peut être qualifiée
d'« initialisation dynamique par agrégat ». L'initialisation par agrégat utilisée par
d doit être utilisée lors de la définition de d, mais avec la
seconde syntaxe il est possible de créer et d'initialiser un objet tableau n'importe où. Par
exemple, supposons que hide() soit une méthode qui accepte un tableau d'objets
Weeble comme argument. On peut l'appeler via :
|
 |
 |
 |
hide(d);
|
 |
hide(d);
|
 |
 |
 |
but you can also dynamically create the array you want to pass as the argument:
|
 |
mais on peut aussi créer dynamiquement le tableau qu'on veut passer comme
argument :
|
 |
 |
 |
hide(new Weeble[] { new Weeble(), new Weeble() });
|
 |
hide(new Weeble[] { new Weeble(), new Weeble() });
|
 |
 |
 |
In some situations this new syntax provides a more convenient way to write code.
|
 |
Cette nouvelle syntaxe est bien plus pratique pour certaines parties de code.
|
 |
 |
 |
The expression
|
 |
L'expression :
|
 |
 |
 |
a = d;
|
 |
a = d;
|
 |
 |
 |
shows how you can take a reference that’s attached to one array object and assign it to another array object, just as you can do with any other type of object reference. Now both a and d are pointing to the same array object on the heap.
|
 |
montre comment prendre une référence attachée à un tableau d'objets et
l'assigner à un autre objet tableau, de la même manière qu'avec n'importe quel type de référence.
Maintenant a et d pointent sur le même tableau d'objets dans le
segment.
|
 |
 |
 |
The second part of ArraySize.java shows that primitive arrays work just like object arrays except that primitive arrays hold the primitive values directly.
|
 |
La seconde partie de ArraySize.java montre que les tableaux de scalaires
fonctionnent de la même manière que les tableaux d'objets sauf que les tableaux de
scalaires stockent directement les valeurs des scalaires.
|
 |
 |
 |
Containers of primitives
|
 |
Conteneurs de scalaires
|
 |
 |
 |
Container classes can hold only references to objects. An array, however, can be created to hold primitives directly, as well as references to objects. It is possible to use the “wrapper” classes such as Integer, Double, etc. to place primitive values inside a container, but the wrapper classes for primitives can be awkward to use. In addition, it’s much more efficient to create and access an array of primitives than a container of wrapped primitives.
|
 |
Les classes conteneurs ne peuvent stocker que des références sur des
objets. Un tableau, par contre, peut stocker directement des scalaires aussi bien que des
références sur des objets. Il est possible d'utiliser des classes d'« encapsulation »
telles qu'Integer, Double, etc. pour stocker des valeurs
scalaires dans un conteneur, mais les classes d'encapsulation pour les types primitifs se révèlent
souvent lourdes à utiliser. De plus, il est bien plus efficace de créer et d'accéder à un tableau
de scalaires qu'à un conteneur de scalaires encapsulés.
|
 |
 |
 |
Of course, if you’re using a primitive type and you need the flexibility of a container that automatically expands when more space is needed, the array won’t work and you’re forced to use a container of wrapped primitives. You might think that there should be a specialized type of ArrayList for each of the primitive data types, but Java doesn’t provide this for you. Some sort of templatizing mechanism might someday provide a better way for Java to handle this problem.[45]
|
 |
Bien sûr, si on utilise un type primitif et qu'on a besoin de la
flexibilité d'un conteneur qui ajuste sa taille automatiquement, le tableau ne convient plus et il
faut se rabattre sur un conteneur de scalaires encapsulés. On pourrait se dire qu'il serait bon
d'avoir un type ArrayList spécialisé pour chacun des types de base, mais ils
n'existent pas dans Java. Un mécanisme de patrons permettra sans doute un jour à Java de mieux
gérer ce problème [45].
|
 |
 |
 |
Returning an array
|
 |
Renvoyer un tableau
|
 |
 |
 |
Suppose you’re writing a method and you don’t just want to return just one thing, but a whole bunch of things. Languages like C and C++ make this difficult because you can’t just return an array, only a pointer to an array. This introduces problems because it becomes messy to control the lifetime of the array, which easily leads to memory leaks.
|
 |
Supposons qu'on veuille écrire une méthode qui ne renvoie pas une seule
chose, mais tout un ensemble de choses. Ce n'est pas facile à réaliser dans des langages tels que C
ou C++ puisqu'ils ne permettent pas de renvoyer un tableau, mais seulement un pointeur sur un
tableau. Cela ouvre la porte à de nombreux problèmes du fait qu'il devient ardu de contrôler la
durée de vie du tableau, ce qui mène très rapidement à des fuites de mémoire.
|
 |
 |
 |
Java takes a similar approach, but you just “return an array.” Actually, of course, you’re returning a reference to an array, but with Java you never worry about responsibility for that array—it will be around as long as you need it, and the garbage collector will clean it up when you’re done.
|
 |
Java utilise une approche similaire, mais permet de « renvoyer un
tableau ». Bien sûr, il s'agit en fait d'une référence sur un tableau, mais Java assume de
manière transparente la responsabilité de ce tableau - il sera disponible tant qu'on en aura
besoin, et le ramasse-miettes le nettoiera lorsqu'on en aura fini avec lui.
|
 |
 |
 |
As an example, consider returning an array of String:
|
 |
Voici un exemple retournant un tableau de String
:
|
 |
 |
 |
//: c09:IceCream.java // Returning arrays from methods.
public class IceCream { static String[] flav = { "Chocolate", "Strawberry", "Vanilla Fudge Swirl", "Mint Chip", "Mocha Almond Fudge", "Rum Raisin", "Praline Cream", "Mud Pie" }; static String[] flavorSet(int n) { // Force it to be positive & within bounds: n = Math.abs(n) % (flav.length + 1); String[] results = new String[n]; boolean[] picked = new boolean[flav.length]; for (int i = 0; i < n; i++) { int t; do t = (int)(Math.random() * flav.length); while (picked[t]); results[i] = flav[t]; picked[t] = true; } return results; } public static void main(String[] args) { for(int i = 0; i < 20; i++) { System.out.println( "flavorSet(" + i + ") = "); String[] fl = flavorSet(flav.length); for(int j = 0; j < fl.length; j++) System.out.println("\t" + fl[j]); } } } ///:~
|
 |
//: c09:IceCream.java // Renvoyer un tableau depuis des méthodes.
public class IceCream { static String[] flav = { "Chocolate", "Strawberry", "Vanilla Fudge Swirl", "Mint Chip", "Mocha Almond Fudge", "Rum Raisin", "Praline Cream", "Mud Pie" }; static String[] flavorSet(int n) { // Force l'argument à être positif & à l'intérieur des indices : n = Math.abs(n) % (flav.length + 1); String[] results = new String[n]; boolean[] picked = new boolean[flav.length]; for (int i = 0; i < n; i++) { int t; do t = (int)(Math.random() * flav.length); while (picked[t]); results[i] = flav[t]; picked[t] = true; } return results; } public static void main(String[] args) { for(int i = 0; i < 20; i++) { System.out.println( "flavorSet(" + i + ") = "); String[] fl = flavorSet(flav.length); for(int j = 0; j < fl.length; j++) System.out.println("\t" + fl[j]); } } } ///:~
|
 |
 |
 |
The method flavorSet( )
creates an array of String called results. The size of this array
is n, determined by the argument you pass into the method. Then it
proceeds to choose flavors randomly from the array flav and place them
into results, which it finally returns. Returning an array is just like
returning any other object—it’s a reference. It’s not
important that the array was created within flavorSet( ), or that
the array was created anyplace else, for that matter. The garbage collector
takes care of cleaning up the array when you’re done with it, and the
array will persist for as long as you need it.
|
 |
La méthode flavorSet() crée un tableau de
Strings de taille n (déterminé par l'argument de la méthode)
appelé results. Elle choisit alors au hasard des parfums dans le tableau
flav et les place dans results, qu'elle renvoie quand elle en a
terminé. Renvoyer un tableau s'apparente à renvoyer n'importe quel autre objet - ce n'est qu'une
référence. Le fait que le tableau ait été créé dans flavorSet() n'est pas
important, il aurait pu être créé n'importe où. Le ramasse-miettes s'occupe de nettoyer le tableau
quand on en a fini avec lui, mais le tableau existera tant qu'on en aura besoin.
|
 |
 |
 |
As an aside, notice that when
flavorSet( ) chooses flavors randomly, it ensures that a random
choice hasn’t been picked before. This is performed in a do loop
that keeps making random choices until it finds one that’s not already in
the picked array. (Of course, a String comparison could also have
been performed to see if the random choice was already in the results
array, but String comparisons are inefficient.) If it’s successful,
it adds the entry and finds the next one (i gets incremented).
|
 |
Notez en passant que quand flavorSet() choisit des parfums
au hasard, elle s'assure que le parfum n'a pas déjà été choisi auparavant. Ceci est réalisé par une
boucle do qui continue de tirer un parfum au sort jusqu'à ce qu'elle en trouve un
qui ne soit pas dans le tableau picked (bien sûr, on aurait pu utiliser une
comparaison sur String avec les éléments du tableau results, mais
les comparaisons sur String ne sont pas efficaces). Une fois le parfum
sélectionné, elle l'ajoute dans le tableau et trouve le parfum suivant (i est
alors incrémenté).
|
 |
 |
 |
main( ) prints out 20 full
sets of flavors, so you can see that flavorSet( ) chooses the
flavors in a random order each time. It’s easiest to see this if you
redirect the output into a file. And while you’re looking at the file,
remember, you just want the ice cream, you don’t need
it.
|
 |
main() affiche 20 ensembles de parfums, et on peut voir
que flavorSet() choisit les parfums dans un ordre aléatoire à chaque fois. Il est
plus facile de s'en rendre compte si on redirige la sortie dans un fichier. Et lorsque vous
examinerez ce fichier, rappelez-vous que vous voulez juste la glace, vous n'en avez pas
besoin.
|
 |
 |
 |
The Arrays class
|
 |
La classe Arrays
|
 |
 |
 |
In java.util, you’ll find
the Arrays class, which holds a set of
static methods that perform utility functions for arrays. There are four
basic functions: equals( ), to compare two arrays for equality;
fill( ), to fill an array with a value; sort( ), to sort
the array; and binarySearch( ), to find an element in a sorted
array. All of these methods are overloaded for all the primitive types and
Objects. In addition, there’s a single asList( ) method
that takes any array and turns it into a List container—which
you’ll learn about later in this chapter.
|
 |
java.util contient la classe Arrays,
qui propose un ensemble de méthodes static réalisant des opérations utiles sur les
tableaux. Elle dispose de quatre fonctions de base : equals(), qui compare
deux tableaux ; fill(), pour remplir un tableau avec une valeur ;
sort(), pour trier un tableau ; et binarySearch(), pour trouver
un élément dans un tableau trié. Toutes ces méthodes sont surchargées pour tous les types scalaires
et les Objects. De plus, il existe une méthode asList() qui
transforme un tableau en un conteneur List - que nous rencontrerons plus tard dans
ce chapitre.
|
 |
 |
 |
While useful, the Arrays class
stops short of being fully functional. For example, it would be nice to be able
to easily print the elements of an array without having to code a for
loop by hand every time. And as you’ll see, the fill( ) method
only takes a single value and places it in the array, so if you wanted—for
example—to fill an array with randomly generated numbers,
fill( ) is no help.
|
 |
Bien que pratique, la classe Arrays montre vite ses
limites. Par exemple, il serait agréable de pouvoir facilement afficher les éléments d'un tableau
sans avoir à coder une boucle for à chaque fois. Et comme nous allons le voir, la
méthode fill() n'accepte qu'une seule valeur pour remplir le tableau, ce qui la
rend inutile si on voulait - par exemple - remplir le tableau avec des nombres
aléatoires.
|
 |
 |
 |
Thus it makes sense to supplement the
Arrays class with some additional utilities, which will be placed in the
package com.bruceeckel.util for convenience. These will print an
array of any type, and fill an array with values or objects that are created by
an object called a generator that you can define.
|
 |
Nous allons donc compléter la classe Arrays avec d'autres
utilitaires, qui seront placés dans le package com.bruceeckel.util. Ces
utilitaires permettront d'afficher un tableau de n'importe quel type, et de remplir un tableau avec
des valeurs ou des objets créés par un objet appelé générateur qu'il est possible de
définir.
|
 |
 |
 |
Because code needs
to be created for each primitive type as well as Object, there’s a
lot of nearly duplicated
code[46]. For
example, a “generator” interface is required for each type because
the return type of next( ) must be different in each
case:
|
 |
Du fait qu'il faille écrire du code pour chaque type scalaire de base aussi
bien que pour la classe Object, une grande majorité de ce code est
dupliqué [46]. Ainsi, par exemple une interface
« générateur » est requise pour chaque type parce que le type renvoyé par
next() doit être différent dans chaque cas :
|
 |
 |
 |
//: com:bruceeckel:util:Generator.java package com.bruceeckel.util; public interface Generator { Object next(); } ///:~
//: com:bruceeckel:util:BooleanGenerator.java package com.bruceeckel.util; public interface BooleanGenerator { boolean next(); } ///:~
//: com:bruceeckel:util:ByteGenerator.java package com.bruceeckel.util; public interface ByteGenerator { byte next(); } ///:~
//: com:bruceeckel:util:CharGenerator.java package com.bruceeckel.util; public interface CharGenerator { char next(); } ///:~
//: com:bruceeckel:util:ShortGenerator.java package com.bruceeckel.util; public interface ShortGenerator { short next(); } ///:~
//: com:bruceeckel:util:IntGenerator.java package com.bruceeckel.util; public interface IntGenerator { int next(); } ///:~
//: com:bruceeckel:util:LongGenerator.java package com.bruceeckel.util; public interface LongGenerator { long next(); } ///:~
//: com:bruceeckel:util:FloatGenerator.java package com.bruceeckel.util; public interface FloatGenerator { float next(); } ///:~
//: com:bruceeckel:util:DoubleGenerator.java package com.bruceeckel.util; public interface DoubleGenerator { double next(); } ///:~
|
 |
//: com:bruceeckel:util:Generator.java package com.bruceeckel.util; public interface Generator { Object next(); } ///:~
//: com:bruceeckel:util:BooleanGenerator.java package com.bruceeckel.util; public interface BooleanGenerator { boolean next(); } ///:~
//: com:bruceeckel:util:ByteGenerator.java package com.bruceeckel.util; public interface ByteGenerator { byte next(); } ///:~
//: com:bruceeckel:util:CharGenerator.java package com.bruceeckel.util; public interface CharGenerator { char next(); } ///:~
//: com:bruceeckel:util:ShortGenerator.java package com.bruceeckel.util; public interface ShortGenerator { short next(); } ///:~
//: com:bruceeckel:util:IntGenerator.java package com.bruceeckel.util; public interface IntGenerator { int next(); } ///:~
//: com:bruceeckel:util:LongGenerator.java package com.bruceeckel.util; public interface LongGenerator { long next(); } ///:~
//: com:bruceeckel:util:FloatGenerator.java package com.bruceeckel.util; public interface FloatGenerator { float next(); } ///:~
//: com:bruceeckel:util:DoubleGenerator.java package com.bruceeckel.util; public interface DoubleGenerator { double next(); } ///:~
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |