t
t
t
t
t t 9) Stockage des objets
tttt
9) Stockage des objets
Texte original t Traducteur : Jérome QUELIN
t
t
    
Ce chapitre contient 14 pages
1 2 3 4 5 6 7 8 9 10 11 12 13 14
\\\
t t t
t t t
t
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
t t t
9: Holding Your Objects t 9 : Stockage des objets
t t t
It’s a fairly simple program that has only a fixed quantity of objects with known lifetimes. t 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.
t t t
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: t 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 :
t t t
MyObject myReference;
t
MyObject myReference;
t t t
since you’ll never know how many of
these you’ll actually need.
t puisqu'on ne connaît pas le nombre exact de références qui seront manipulées.
t t t
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.
t 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.
t t t


Arrays


t

Les tableaux

t t t
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?
t 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 ?
t t t
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.
t 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.
t t t
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.
t 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.
t t t
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.
t 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.
t t t
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.
t 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.
t t t


Arrays are first-class
objects


t

Les tableaux sont des objets

t t t
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.
t 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.
t t t
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.

t 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.
t t t
//: 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);
  }
} ///:~
t
//: 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);
  }
} ///:~
t t t
Here’s the output from the program: t Voici la sortie du programme :
t t t
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
t
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
t t t
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. t 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.
t t t
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. t 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.
t t t
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: t 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 :
t t t
hide(d);
t
hide(d);
t t t
but you can also dynamically create the
array you want to pass as the argument:

t mais on peut aussi créer dynamiquement le tableau qu'on veut passer comme argument :
t t t
hide(new Weeble[] { new Weeble(), new Weeble() });
t
hide(new Weeble[] { new Weeble(), new Weeble() });
t t t
In some situations this new syntax
provides a more convenient way to write code.
t Cette nouvelle syntaxe est bien plus pratique pour certaines parties de code.
t t t
The expression

t L'expression :
t t t
a = d;
t
a = d;
t t t
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.
t 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.
t t t
The second part of ArraySize.java
shows that primitive arrays work just like object arrays except that
primitive arrays hold the primitive values
directly.
t 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.
t t t


Containers of primitives


t

Conteneurs de scalaires

t t t
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.
t 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.
t t t
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]
t 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].
t t t


Returning an array


t

Renvoyer un tableau

t t t
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.
t 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.
t t t
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.
t 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.
t t t
As an example, consider returning an
array of String:

t Voici un exemple retournant un tableau de String :
t t t
//: 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]);
    }
  }
} ///:~
t
//: 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]);
    }
  }
} ///:~
t t t
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. t 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.
t t t
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). t 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é).
t t t
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. t 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.
t t t

The Arrays class

t

La classe Arrays

t t t
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. t 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.
t t t
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. t 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.
t t t
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. t 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.
t t t
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: t 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 :
t t t
//: 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();
} ///:~
t
//: 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();
} ///:~
t t t
t t t
t t
    
///
t t t
t
     
Sommaire Le site de Bruce Eckel