Les Collections : Manipuler les tableaux, introduction à NSArray et NSMutableArray

Sylvain Gamel

Les tableaux d'objets sont une structure de données courante et très souvent utilisées.

Qu'est-ce qu'un tableau ?

Un tableau est une liste ordonnée d'objets où chaque objet peut être accédé par sa position dans le tableau : son index.

Java et Cocoa proposent évidemment des classes pour mettre en œuvre ces structures de données.

Cet article se propose d'introduire rapidement les principales fonctionnalités offertes en Cocoa et de ce fait présente rapidement les lignes directrices adoptées par chacun des deux langages.

Commentez

- Titre : Les Collections : Manipuler les tableaux, introduction à NSArray et NSMutableArray

- Auteur : Sylvain Gamel

- Parution : 21 avril 2010

- Mise à jour : 4 janvier 2011

- Licence : Le contenu de cet article a été rédigé par Sylvain Gamel et est disponible sous la licence Creative Commons "Paternité Pas d'Utilisation Commerciale" Par contre, les logos developpez.com, en-tête, pied de page, css et look & feel de l'article sont eux sous copyright 2010 developpez.com

Sommaire

- I. Principes Généraux

- II. Types de tableaux

- III. Création de tableaux

- IV. Accès aux éléments

- V. Itération sur les collections

- VI. Trier les données

- VII. Conclusion

- VIII. Documents joints

I - Principes Généraux

Java propose une bibliothèque d'interfaces et de classes relativement riche pour les collections d'objets.

La démarche de Java reste de proposer une interface et des implémentations standards..

De plus, et dans la logique d'un typage fort du langage, Java a introduit la notion de type générique déjà connue des développeurs C++. Ceci a pour objectif de contraindre le type des objets qu'une collection peut contenir.

Objective-C et Cocoa suivent un mode de fonctionnement opposé en limitant le typage fort et en ne proposant que des implémentations des principaux types de collections.

Cocoa ne propose pas de protocoles (interfaces) pour chaque type de collection. C'est en revanche une série compacte de classes qui est proposée. Moins riche probablement que dans les API Java, mais concentrée sur l'essentiel.

II - Types de tableaux

On peut distinguer deux catégories de types de tableaux :

- le type tableau proposé comme un type du langage. La déclaration et l'accès aux éléments utilise les crochets dans les deux cas.

- les classes "tableau" des bibliothèques de développement associées au langage.

Si le type tableau existe bien dans les deux langages, il n'est pas traité de la même façon. En Java le type est mis en œuvre par une classe interne de la machine virtuelle. Ce n'est pas le cas en Objective-C.

Le tableau se caractérise par un temps d'accès constant à chaque élément grâce à son organisation linéaire et un accès indexé.

Cocoa propose de différencier les tableaux qui peuvent être modifiés de ceux qui ne sont jamais modifiés. Ce modèle de conception est appliqué dans de nombreuses classes de Cocoa et permet de proposer des implémentations optimisées.

Il est ainsi possible pour une classe de retourner toujours la même instance si la valeur est identique.

Cocoa propose deux classes de tableaux :

- NSArray le tableau uniquement disponible en consultation.

- NSMutableArray le tableau dont le contenu peut être modifié.

Une troisième classe de tableaux est particulièrement dédiée au stockage de pointeurs vers de objets. NSPointerArray a la particularité de pouvoir contenir des références NULL.

Java propose pour sa part plusieurs classes comme :

- java.util.ArrayList<E> ;

- java.util.Vector<E>. Chacune de ces classes étant une implémentation de l'interface java.util.List<E>.

On peut remarquer que, contrairement à Java, Cocoa ne propose pas un protocole de base pour les différentes collections. Seul le protocole utilisé par l'itération est défini.

III - Création de tableaux

Comme en Java, Objective-C ne permet pas de construire un tableau à partir d'un variable de type tableau. Il existe cependant des solutions similaires.

Ainsi il est possible d'initialiser un tableau directement dans votre code avec un constructeur utilitaire arrayWithObjects :. Dans ce cas la liste des éléments doit être terminée par la valeur spécifique nil.

NSArray * tableSimple = [NSArray arrayWithObjects: @"Un",
@"tableau",
@"simple",
@"mais",
@"non",
@"modifiable",
nil];

Attention : Un constructeur porte le même nom au pluriel près. Cette variante ne construit qu'un tableau d'un seul élément et n'accepte qu'un seul paramètre.

IV - Accès aux éléments

L'accès à un élément de tableau en Java suit le même modèle de conception que les JavaBeans :

- get(idx) pour obtenir un élément à la positon idx ;

- set(idx, objet) pour modifier l'élément à la positon idx.

Le principe est similaire en Cocoa :

- objectAtIndex : retourne l'objet stocké à l'index indiqué ;

- replaceObjectAtIndex:withObject : remplace l'objet stocké à l'index indiqué ;

- insertObject:atIndex : insère un objet à l'index indiqué ;

Exemple :

[tableModifiable objectAtIndex: 2]
[tableModifiable replaceObjectAtIndex: 2
withObject: @"basique"];
[tableModifiable insertObject: @"de chaines"
atIndex: 3];

Le nombre d'éléments du tableau est obtenu en accédant à la propriété count d'un NSArray. En Java, ce sera l'attribut length qui sera utilisé.

V - Itération sur les collections

Les  deux langages proposent une syntaxe simplifiée pour parcourir l'ensemble des objets d'une collection. Chacune de ces syntaxe s'appuie sur des interface spécifiques qui doivent être implémentée par l'objet parcouru.

Syntaxe d'une itération sur une collection :

- Java : for ( Type Var : Collection ) { ... }

- Objective-C : for ( TypeVar in Expression ) { ... }

Contrainte sur la collection :

- Java : interface java.lang.Iterable<T>

- Objective-C : protocole NSFastEnumeration

Comme tout objet d'une classe qui implémente le protocole NSFastEnumeration, il est donc très simple de parcourir un tableau.

int idx = 0;
for ( id anItem in tableSimple )
{
NSLog(@"Élément %d : %@", idx, anItem);
idx += 1;
}

L'équivalent Java serait :

int idx = 0;
for ( String uneChaine : tableauNonModifiable )
{
System.out.println("Elément "+idx+" = '"+uneChaine+"'");
idx += 1;
}

VI - Trier les données

Commençons par un rappel du principe de fonctionnement en Java :

- La classe Collections propose une méthode sort()

- Les éléments de la collection doivent simplement implémenter l'interface Comparable

Le fonctionnement en Cocoa n'est pas très éloigné et les différences mettent, une fois de plus, l'accent sur les différences philosophiques entre Java et Objective-C en mettant la priorité sur les sélecteurs plutôt que les protocoles :

- La classe NSMutableArray propose une méthode sortUsingSelector : ;

- Les éléments du tableau doivent proposer une méthode implémentant le sélecteur indiqué à la méthode de tri.

D'autres méthodes existent qui n'utilisent pas les sélecteurs :

- sortUsingComparator : a pour paramètre un bloc NSComparator

- sortUsingDescriptors :a pour paramètre un NSArray de NSSortDescriptor ;

- sortUsingFunction:Context : les paramètres sont alors une fonction C et un pointeur vers un objet contexte.

D'autres variantes existent également pour préciser des options particulières :

- sortWithOptions:usingComparator : ;

- sortWithSortDescriptors:recursively :

Je vous renvoie pour plus de détails aux références de ces sélecteurs pour les classes NSMutableArray et NSMatrix.

Remarque : Le tableau étant directement modifié, seules les collections mutable proposent cette fonctionnalité.

En pratique, pour trier un tableau de chaines de texte il suffit ainsi de quelques lignes :

SEL selectCompareTexte =
@selector(localizedCaseInsensitiveCompare:);
[tableModifiable sortUsingSelector: selectCompareTexte];

Dans cet exemple nous utilisons le sélecteur localizedCaseInsensitiveCompare : qui est mis en œuvre dans NSString et permet de comparer deux chaines sans tenir compte de la casse (majuscules/minuscules) et en prenant en compte les spécificités linguistiques.

Rien de très différent d'une solution Java s'appuyant sur l'implémentation de Comparable dans String.

VII - Conclusion

Alors que les API de Java proposent un framework complet construit sur des interfaces fortement typées ; Objective-C et Cocoa se recentrent sur un minimum de classes. Une fois de plus ce sont les sélecteurs de méthodes qui sont à l'honneur pour les fonctionnalités.

Deux projets exemple, l'un en Java et l'autre en Cocoa, sont joints à cet article.

VIII - Documents joints

Exemple Java (Miroir) : Un projet NetBeans pour un exemple très simple d'utilisation des tableaux en Java.

Exemple Objective-C (Miroir) : Un projet XCode illustrant les principaux cas d'utilisation de NSArray.