IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java

  Chapitre 9 - Stockage des objets

pages : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 

List x = new ArrayList();

Et on ne touche pas au reste du code (une telle généricité peut aussi être réalisée via des itérateurs).

Dans la hiérarchie de classes, on peut voir un certain nombre de classes dont le nom débute par « Abstract », ce qui peut paraître un peu déroutant au premier abord. Ce sont simplement des outils qui implémentent partiellement une interface particulière. Si on voulait réaliser notre propre Set, par exemple, il serait plus simple de dériver AbstractSet et de réaliser le travail minimum pour créer la nouvelle classe, plutôt que d'implémenter l'interface Set et toutes les méthodes qui vont avec. Cependant, la bibliothèque de conteneurs possède assez de fonctionnalités pour satisfaire quasiment tous nos besoins. De notre point de vue, nous pouvons donc ignorer les classes débutant par « Abstract ».

Ainsi, lorsqu'on regarde le diagramme, on n'est réellement concerné que par les interfaces du haut du diagramme et les classes concrètes (celles qui sont entourées par des boîtes solides). Typiquement, on se contentera de créer un objet d'une classe concrète, de la transtyper dans son interface correspondante, et ensuite utiliser cette interface tout au long du code. De plus, on n'a pas besoin de se préoccuper des éléments pré-existants lorsqu'on produit du nouveau code. Le diagramme peut donc être grandement simplifié pour ressembler à ceci :    

Image

Il n'inclut plus maintenant que les classes et les interfaces que vous serez amenés à rencontrer régulièrement, ainsi que les éléments sur lesquels nous allons nous pencher dans ce chapitre.

Voici un exemple simple, qui remplit une Collection (représenté ici par une ArrayList) avec des objets String, et affiche ensuite chaque élément de la Collection :

//: c09:SimpleCollection.java
// Un exemple simple d'utilisation des Collections Java 2.
import java.util.*;

public class SimpleCollection {
  public static void main(String[] args) {
    // Transtypage ascendant parce qu'on veut juste
    // travailler avec les fonctionnalités d'une Collection
    Collection c = new ArrayList();
    for(int i = 0; i < 10; i++)
      c.add(Integer.toString(i));
    Iterator it = c.iterator();
    while(it.hasNext())
      System.out.println(it.next());
  }
} ///:~

La première ligne de main() crée un objet ArrayList et le transtype ensuite en une Collection. Puisque cet exemple n'utilise que les méthodes de Collection, tout objet d'une classe dérivée de Collection fonctionnerait, mais l'ArrayList est la Collection à tout faire typique.

La méthode add(), comme son nom le suggère, ajoute un nouvel élément dans la Collection. En fait, la documentation précise bien que add() « assure que le conteneur contiendra l'élément spécifié ». Cette précision concerne les Sets, qui n'ajoutent un élément que s'il n'est pas déjà présent. Avec une ArrayList, ou n'importe quel type de List, add() veut toujours dire « stocker dans », parce qu'une List se moque de contenir des doublons.

Toutes les Collections peuvent produire un Iterator grâce à leur méthode iterator(). Ici, un Iterator est créé et utilisé pour traverser la Collection, en affichant chaque élément.

Fonctionnalités des Collections

La table suivante contient toutes les opérations définies pour une Collection (sans inclure les méthodes directement héritées de la classe Object), et donc pour un Set ou une List (les Lists possèdent aussi d'autres fonctionnalités). Les Maps n'héritant pas de Collection, elles seront traitées séparément.  

boolean add(Object) Assure que le conteneur stocke l'argument. Renvoie false si elle n'ajoute pas l'argument (c'est une méthode « optionnelle », décrite plus tard dans ce chapitre).
boolean addAll(Collection) Ajoute tous les éléments de l'argument. Renvoie true si un élément a été ajouté (« optionnelle »).
void clear() Supprime tous les éléments du conteneur (« optionnelle »).
boolean contains(Object) true si le conteneur contient l'argument.
boolean containsAll(Collection) true si le conteneur contient tous les éléments de l'argument. 
boolean isEmpty() true si le conteneur ne contient pas d'éléments. 
Iterator iterator() Renvoie un Iterator qu'on peut utiliser pour parcourir les éléments du conteneur.
boolean remove(Object) Si l'argument est dans le conteneur, une instance de cet élément est enlevée. Renvoie true si c'est le cas (« optionnelle »).
boolean removeAll(Collection) Supprime tous les éléments contenus dans l'argument. Renvoie true si au moins une suppression a été effectuée (« optionnelle »).
boolean retainAll(Collection) Ne garde que les éléments contenus dans l'argument (une « intersection » selon la théorie des ensembles). Renvoie true s'il y a eu un changement (« optionnelle »).
int size() Renvoie le nombre d'éléments dans le conteneur. 
Object[] toArray() Renvoie un tableau contenant tous les éléments du conteneur.
Object[] toArray(Object[] a) Renvoie un tableau contenant tous les éléments du conteneur, dont le type est celui du tableau a au lieu d'Objects génériques (il faudra toutefois transtyper le tableau dans son type correct).

Notez qu'il n'existe pas de fonction get() permettant un accès aléatoire. Ceci parce que les Collections contiennent aussi les Sets, qui maintiennent leur propre ordre interne, faisant de toute tentative d'accès aléatoire un non-sens. Il faut donc utiliser un Iterator pour parcourir tous les éléments d'une Collection ; c'est la seule façon de récupérer les objets stockés.

L'exemple suivant illustre toutes ces méthodes. Encore une fois, cet exemple marcherait avec tout objet héritant de Collection, mais nous utilisons ici une ArrayList comme « plus petit dénominateur commun » :

//: c09:Collection1.java
// Opérations disponibles sur les Collections.
import java.util.*;
import com.bruceeckel.util.*;

public class Collection1 {
  public static void main(String[] args) {
    Collection c = new ArrayList();
    Collections2.fill(c,
      Collections2.countries, 10);    
    c.add("ten");
    c.add("eleven");
    System.out.println(c);
    // Crée un tableau à partir de la List :
    Object[] array = c.toArray();
    // Crée un tableau de Strings à partir de la List :
    String[] str =
      (String[])c.toArray(new String[1]);
    // Trouve les éléments mini et maxi ; ceci peut
    // signifier différentes choses suivant la manière
    // dont l'interface Comparable est implémentée :
    System.out.println("Collections.max(c) = " +
      Collections.max(c));
    System.out.println("Collections.min(c) = " +
      Collections.min(c));
    // Ajoute une Collection à une autre Collection
    Collection c2 = new ArrayList();
    Collections2.fill(c2,
      Collections2.countries, 10);    
    c.addAll(c2);
    System.out.println(c);
    c.remove(CountryCapitals.pairs[0][0]);
    System.out.println(c);
    c.remove(CountryCapitals.pairs[1][0]);
    System.out.println(c);
    // Supprime tous les éléments
    // de la Collection argument :
    c.removeAll(c2);
    System.out.println(c);
    c.addAll(c2);
    System.out.println(c);
    // Est-ce qu'un élément est dans la Collection ?
    String val = CountryCapitals.pairs[3][0];
    System.out.println(
      "c.contains(" + val  + ") = "
      + c.contains(val));
    // Est-ce qu'une Collection est contenue dans la Collection ?
    System.out.println(
      "c.containsAll(c2) = "+ c.containsAll(c2));
    Collection c3 = ((List)c).subList(3, 5);
    // Garde les éléments présents à la fois dans
    // c2 et c3 (intersection d'ensembles) :
    c2.retainAll(c3);
    System.out.println(c);
    // Supprime tous les éléments
    // de c2 contenus dans c3 :
    c2.removeAll(c3);
    System.out.println("c.isEmpty() = " +
      c.isEmpty());
    c = new ArrayList();
    Collections2.fill(c,
      Collections2.countries, 10);    
    System.out.println(c);
    c.clear(); // Supprime tous les éléments
    System.out.println("after c.clear():");
    System.out.println(c);
  }
} ///:~

Les ArrayLists sont créées et initialisées avec différents ensembles de données, puis transtypées en objets Collection ; il est donc clair que seules les fonctions de l'interface Collection sont utilisées. main() réalise de simples opérations pour illustrer toutes les méthodes de Collection.

Les sections suivantes décrivent les diverses implémentations des Lists, Sets et Maps et indiquent dans chaque cas (à l'aide d'une astérisque) laquelle devrait être votre choix par défaut. Vous noterez que les classes pré-existantes Vector, Stack et Hashtable ne sont pas incluses car certains conteneurs Java 2 fournissent les mêmes fonctionnalités.

Fonctionnalités des Lists

La List de base est relativement simple à utiliser, comme vous avez pu le constater jusqu'à présent avec les ArrayLists. Mis à part les méthodes courantes add() pour insérer des objets, get() pour les retrouver un par un, et iterator() pour obtenir un Iterator sur la séquence, les listes possèdent par ailleurs tout un ensemble de méthodes qui peuvent se révéler très pratiques.

Les Lists sont déclinées en deux versions : l'ArrayList de base, qui excelle dans les accès aléatoires aux éléments, et la LinkedList, bien plus puissante (qui n'a pas été conçue pour un accès aléatoire optimisé, mais dispose d'un ensemble de méthodes bien plus conséquent).  

List (interface) L'ordre est la caractéristique la plus importante d'une List ; elle garantit de maintenir les éléments dans un ordre particulier. Les Lists disposent de méthodes supplémentaires permettant l'insertion et la suppression d'éléments au sein d'une List (ceci n'est toutefois recommandé que pour une LinkedList). Une List produit des ListIterators, qui permettent de parcourir la List dans les deux directions, d'insérer et de supprimer des éléments au sein de la List.
ArrayList* Une List implémentée avec un tableau. Permet un accès aléatoire instantané aux éléments, mais se révèle inefficace lorsqu'on insère ou supprime un élément au milieu de la liste. Le ListIterator ne devrait être utilisé que pour parcourir l'ArrayList dans les deux sens, et non pour l'insertion et la suppression d'éléments, opérations coûteuses comparées aux LinkedLists.
LinkedList Fournit un accès séquentiel optimal, avec des coûts d'insertion et de suppression d'éléments au sein de la List négligeables. Relativement lente pour l'accès aléatoire (préférer une ArrayList pour cela). Fournit aussi les méthodes addFirst(), addLast(), getFirst(), getLast(), removeFirst() et removeLast() (qui ne sont définies dans aucune interface ou classe de base) afin de pouvoir l'utiliser comme une pile, une file (une queue) ou une file double (queue à double entrée).

Les méthodes dans l'exemple suivant couvrent chacune un groupe de fonctionnalités : les opérations disponibles pour toutes les listes (basicTest()), le déplacement dans une liste avec un Iterator (iterMotion()) ainsi que la modification dans une liste avec un Iterator (iterManipulation()), la visualisation des manipulations sur la List (testVisual()) et les opérations disponibles uniquement pour les LinkedLists.

//: c09:List1.java
// Opérations disponibles sur les Lists.
import java.util.*;
import com.bruceeckel.util.*;

public class List1 {
  public static List fill(List a) {
    Collections2.countries.reset();
    Collections2.fill(a,
      Collections2.countries, 10);
    return a;
  }
  static boolean b;
  static Object o;
  static int i;
  static Iterator it;
  static ListIterator lit;
  public static void basicTest(List a) {
    a.add(1, "x"); // Ajout à l'emplacement 1
    a.add("x"); // Ajout à la fin
    // Ajout d'une Collection :
    a.addAll(fill(new ArrayList()));
    // Ajout d'une Collection à partir du 3ème élément :
    a.addAll(3, fill(new ArrayList()));
    b = a.contains("1"); // L'élément est-il présent ?
    // La Collection entière est-elle présente ?
    b = a.containsAll(fill(new ArrayList()));
    // Les Lists permettent un accès aléatoire aux éléments,
    // bon marché pour les ArrayLists, coûteux pour les LinkedLists :
    o = a.get(1); // Récupère l'objet du premier emplacement
    i = a.indexOf("1"); // Donne l'index de l'objet
    b = a.isEmpty(); // La List contient-elle des éléments ?
    it = a.iterator(); // Iterator de base
    lit = a.listIterator(); // ListIterator
    lit = a.listIterator(3); // Démarre au 3ème élément
    i = a.lastIndexOf("1"); // Dernière concordance
    a.remove(1); // Supprime le premier élément
    a.remove("3"); // Supprime cet objet
    a.set(1, "y"); // Positionne le premier élément à "y"
    // Garde tous les éléments présents dans l'argument
    // (intersection de deux ensembles) :
    a.retainAll(fill(new ArrayList()));
    // Supprime tous les éléments présents dans l'argument :
    a.removeAll(fill(new ArrayList()));
    i = a.size(); // Taille de la List ?
    a.clear(); // Supprime tous les éléments
  }
  public static void iterMotion(List a) {
    ListIterator it = a.listIterator();
    b = it.hasNext();
    b = it.hasPrevious();
    o = it.next();
    i = it.nextIndex();
    o = it.previous();
    i = it.previousIndex();
  }
  public static void iterManipulation(List a) {
    ListIterator it = a.listIterator();
    it.add("47");
    // Doit aller sur un élément après add() :
    it.next();
    // Supprime l'élément qui vient d'être produit :
    it.remove();
    // Doit aller sur un élément après remove() :
    it.next();
    // Change l'élément qui vient d'être produit :
    it.set("47");
  }
  public static void testVisual(List a) {
    System.out.println(a);
    List b = new ArrayList();
    fill(b);
    System.out.print("b = ");
    System.out.println(b);
    a.addAll(b);
    a.addAll(fill(new ArrayList()));
    System.out.println(a);
    // Insère, supprime et remplace des éléments
    // en utilisant un ListIterator :
    ListIterator x = a.listIterator(a.size()/2);
    x.add("one");
    System.out.println(a);
    System.out.println(x.next());
    x.remove();
    System.out.println(x.next());
    x.set("47");
    System.out.println(a);
    // Traverse la liste à l'envers :
    x = a.listIterator(a.size());
    while(x.hasPrevious())
      System.out.print(x.previous() + " ");
    System.out.println();
    System.out.println("testVisual finished");
  }
  // Certaines opérations ne sont disponibles
  // que pour des LinkedLists :
  public static void testLinkedList() {
    LinkedList ll = new LinkedList();
    fill(ll);
    System.out.println(ll);
    // Utilisation comme une pile, insertion (push) :
    ll.addFirst("one");
    ll.addFirst("two");
    System.out.println(ll);
    // Utilisation comme une pile, récupération de la valeur du premier élément (peek) :
    System.out.println(ll.getFirst());
    // Utilisation comme une pile, suppression (pop) :
    System.out.println(ll.removeFirst());
    System.out.println(ll.removeFirst());
    // Utilisation comme une file, en retirant les
    // éléments à la fin de la liste :
    System.out.println(ll.removeLast());
    // Avec les opérations ci-dessus, c'est une file double !
    System.out.println(ll);
  }
  public static void main(String[] args) {
    // Crée et remplit une nouvelle List à chaque fois :
    basicTest(fill(new LinkedList()));
    basicTest(fill(new ArrayList()));
    iterMotion(fill(new LinkedList()));
    iterMotion(fill(new ArrayList()));
    iterManipulation(fill(new LinkedList()));
    iterManipulation(fill(new ArrayList()));
    testVisual(fill(new LinkedList()));
    testLinkedList();
  }
} ///:~

Ce livre a été écrit par Bruce Eckel ( télécharger la version anglaise : Thinking in java )
Ce chapitre a été traduit par Jérome Quelin ( groupe de traduction )
télécharger la version francaise (PDF) | Commandez le livre en version anglaise (amazon) | télécharger la version anglaise
pages : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java