NSDictionary et NSTableView
Flo
Membre
Bonjour à tous,
J'ai un NSMutableDictionary qui référence un certain nombre d'objet ayant chacun un certain nombre de champs.
Ce que je souhaiterai faire c'est présenter ces objets à l'utilisateur de la manière suivante : Objet1.champ1 .... Objet1.champn puis objetN.champ1 .... ObjectN.champn
J'ai naturellement pensé à NSTableView qui fait parfaitement cela mais à priori c'est peu adapté à la classe NSDictionary. Quelqu'un aurait-il une idée pour afficher mes objet de la manière la plus efficace possible ?
Merci d'avance pour vos réponses !
J'ai un NSMutableDictionary qui référence un certain nombre d'objet ayant chacun un certain nombre de champs.
Ce que je souhaiterai faire c'est présenter ces objets à l'utilisateur de la manière suivante : Objet1.champ1 .... Objet1.champn puis objetN.champ1 .... ObjectN.champn
J'ai naturellement pensé à NSTableView qui fait parfaitement cela mais à priori c'est peu adapté à la classe NSDictionary. Quelqu'un aurait-il une idée pour afficher mes objet de la manière la plus efficace possible ?
Merci d'avance pour vos réponses !
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Et si c'est ça, du coup tu veux afficher tes données comment ? Sous forme d'un truc hiérarchique (comme dans la vue liste dans le Finder où tu peux afficher le contenu d'un dossier avec un petit triangle) ? Ou tu vois ça autrement ?
Le NSMutableDictionary contient des objets chacun identifié par une clé (NSString), un petit schéma s'impose :
(@clé1, monObjet1, @clé2, monObjet2, @cléN, monObjetN).
Les objets monObjet sont du même type et contiennent tous les mêmes champs.
Moi je voudrait afficher simplement le contenu du NSMutableDictionary, genre:
colonne0 colonne1 colonne2 colonne3
@clé1 monObjet1.champ1 monObjet1.champ2 monObjet1.champn
@clé2 monObjet2.champ1 monObjet2.champ2 monObjet2.champn
@cléN monObjetN.champ1 monObjetN.champ2 monObjetN.champn
J'ai lu sur le net qu'il y avait deux solutions pour faire ça dans une NSTableView :
- utiliser un NSArray avec toutes les clés du NSMutableDictionary
- utiliser un NSArray de NSDictionary
Je trouve ces solutions lourdes et difficiles à mettre en place, l'idéal serait une solution qui ne rajoute pas de structure de données à mon NSMutableDictionary existant.
C'est plus clair là ou pas ?
la voiture de: nbRoue couleur commentaire
@maVoiture 4 verte une Dodge Viper
@saVoiture 2 on sait plus à la casse
@uneVoiture 5 turquoise pas encore sortie
Sinon ce que tu souhaites faire est tout à fait envisageable, et ce sans rajouter d'objet modèle supplémentaire : pour savoir quel objet récupérer en fonction de la ligne, il suffit de récupérer le tableau "allKeys" de ton NSDictionary. Après il suffit d'affecter à tes colonnes l'identifier "key" pour la première, "champ1" pour la 2e, "champ2" pour la 3e... Et si tes objets contiennent bien une propriété nommée "champ1" (à laquelle tu peux accéder grâce à valueForKey, merci le KVC), cette valeur s'affichera dans la colonne adéquate, et pareil pour les autres.
Je souhaite ainsi profiter de l'accès en O(1) (hachage) d'un objet à partir de sa clé (performance des recherches + mise à jour dans ma base).
Oui je m'étais penché sur cette solution mais elle ma semblée peu réaliste, voilà ce que dit la doc sur cette méthode :
Imaginons j'ai 5000 objets dans mon NSMutableDictionary, il va falloir faire 5000 copies des 5000 clés à chaque fois ? C'est pas terrible niveau perfs...
De plus, même s'il n'y avait pas cette optimisation, un NSArray est un tableau indexé, pas une liste chaà®née. Donc l'accès à l'élément N d'un NSArray est en O(1) (c'est l'insertion/suppression à la limite qui peut ne pas être en O(1) si c'est des éléments en plein milieu).
D'ailleurs pour les NSDictionary et donc une table de hachage je dirais plutôt que c'est en O(ln(n)) plutôt qu'en O(1), vu que d'une part il faut trouver la liste chaà®née des valeurs dont le hash correspond au hash de l'objet recherché, puis parcourir enfin cette liste chaà®née... Alors qu'avec un tableau indexé, on récupère l'objet qui se trouve pointé par l'adresse (&tableau + requestedIndex) en gros hein. Donc linéaire, lui.
Source: RidiculousFish
Ouh là non non non ! Eventuellement déjà je me demande si les NSDictionary ne contiennent pas déjà un tableau de keys, donc il ne ferait que te le retourner. Mais même si ce n'est pas le cas, cela ne va pas faire une copie des 5000 clés !! Le referenceCounting est justement là pour ça, encore heureux Ca ne va donc fait qu'un "retain" sur les clés lorsqu'il va construire le NSArray (mais ça restera les mêmes objets, les mêmes pointeurs, donc pas d'allocation pour dupliquer les clés en mémoire quand même !!)
Faut pas oublier que Cocoa est quand même bien foutu, et qu'en particulier les classes les plus communes et utilisées comme NSString ou les classes de collections NSArray/NSDictionary/NSSet/... sont quand même pas mal optimisées (cf le lien plus haut)
Ya un truc qu'est quasiment certain quand même, c'est que la recherche d'un élément à partir d'une clé est plus rapide dans un NSDictionary que dans un NSArray sinon je vois pas l'intérêt de cette première classe...
Après je le concède, est-ce que la différence vaut le coup quand on voit les difficultés que ça génère pour exploiter le NSDictionary dans une NSTableView.
Je sais pas si c'est plus rapide ou pas... je pense pas. Une NSArray si tu veux faire une recherche rapide il te suffit de faire un NSSortDescriptor avec un compare: en utilisant par exemple "hasPrefix:" de NSString. Ensuite tu fais ta fast enumeration et tu peux faire un break dès que tu rencontres le premier élément qui ne rempli plus la condition "hasPrefix:mySearch".
Un NSDictionary c'est autre chose... mais ça dépend comment tu le construit je pense... Si tu fais une NSArray qui contient des NSDictionary, là encore tu peux utiliser NSSortDescriptor.. le seul truc c'est qu'il se peut que tu veuilles effectuer la recherche sur plusieurs clés du dico, donc tu devras faire plusieurs sortDescriptors.
D'ailleurs concernant la recherche, au lieu d'utiliser hasPrefix, tu peux aussi utiliser "rangeOfString:" qui te retournes (NSNotFound, 0) si jamais il ne trouve pas.
Il reste toujours la question de savoir si un NSSortDescriptor est rapide. Personnellement je pense que c'est vraiment plus rapide qu'une énumération (me semble meme que la question a déjà été évoquée?).
Bref, je te conseille quand meme de construire ta TableView avec NSArray qui contient des NSDictionary avec les clés qui représentent les column du tableau.
(Et puis je t'assure que même avec 5000 références, ta recherche ne durera qu'une seconde même pas).
Et donc comme Apple a sû optimiser tout ce qu'il y a en dessous pour que l'accès aux éléments d'un NSArray soit optimal (au même titre que pour un NSDictionary, note)... bah du coup certes les NSDictionary ont par concept une table de hashage sous le capot ça c'est un peu le principe... mais ça n'empêche pas d'avoir des NSArray super rapides pour les temps d'accès (sachant que ce n'est pas le même en random ou séquentiel bien sûr, et que c'est encore plus rapide si tu utilises la NSFastEnumaration "foreach" avec Objective-C 2.0)...
Donc au final vu tout ce qui est mis en place sous le capot qui fait que ce genre de trucs est super optimisé, au point que la notion de "NSArray" et "NSDictionary" soit surtout conceptuelle (= un NSArray c'est pour stocker une liste/un tableau de données, alors qu'un NSDictionary c'est un ensemble de paires clé/valeur non ordonné) mais que ça ne présage pas de l'implémentation sous-jacente de ces classes pour autant (d'où le titre "our arrays.... that aren't" de l'article)...
Tu risques + de te prendre les pieds dans le tapis à réfléchir sous la forme "l'implémentation sous-jacente devrait être une table de hashage donc optimisée en O(log(n)) donc devrait être plus rapide dans tel cas de figure..." alors que y'a tellement de paramètres à prendre en compte (accès séquentiel, random, optimisation interne selon le nombre d'éléments, type de boucle for/foreach pour accès aux éléments de la collection lors de boucles...) que c'est loin d'être aussi simple en plus...
Ce sera donc un NSArray ! Merci pour votre aide qui fut (comme toujours) très instructive et qui fait que c'est toujours un plaisir de poster une question sur ce forum !
Un tableau a pour but d'associer un nombre plus ou moins grand de valeur à un index, mais dans un tableau théoriquement fini.
En revanche, un dictionaire contient en théorie toutes les clés de l'univers, mais toutes ces clés ne sont pas forcément associées à une valeur, et c'est ça la grosse différence.
Le dictionaire n'a de réel intérêt que si tu as peu de valeurs pour beaucoup de clés possibles, par exemple, mettons que tu aies trois valeurs obj1, obj2 et obj3 ayant pour index respectifs 100, 45500, et 856410...
Si tu voulais stocker ces valeurs dans un tableau, il te faudrait un tableau de 856.410 valeur avec tout de même 856.407 cases vides ! C'est idiot, alors dans ce cas là tu utiliserais un dictionaire avec 3 valeurs et 3 clés de type nombre.
C'est vraiment le seul avantage d'un dictionaire par rapport à un tableau, c'est que tu as une infinité de clés. C'est pour ça d'ailleurs que lorsque tu fais [myArray objectAtIndex: idx] avec une valeur supérieure au -count de ton tableau tu as une exception, alors que si tu fais [myDict objectForKey: key] avec une clé qui n'est associée à rien, tu reçois "nil", et ça c'est le cas dans toutes les implémentations. Tout simplement parce qu'on suppose que le dictionnaire possède toutes les clés mais qu'elles ne sont pas toutes associées à une valeur.
La vitesse n'est pas du tout l'avantage d'un dictionaire.
Et pour ce qui est de la méthode -allKeys, le problème de celle-ci c'est qu'elle ne garantit pas de retourner les clés dans le même ordre, si tu changes une clé, tu risques d'avoir un ordre totalement différent.
Autre chose, il faut remarquer que le dictionaire, par mesure de sécurité, copie les clés qu'on lui passe, donc pour commencer une clé est toujours conforme au protocol NSCopying, et ensuite ça signifie, pour les objets modifiables utilisés comme clés, qu'en utiliser -allKeys le dictionaire va sûrement créer une nouvelle instance de l'objet.
Cependant, ce problème est réglé en utilisant des clés de type NSString, puisque la copie d'un objet non-modifiable ne fait qu'un retain.
PS :
En regardant le tableau, c'est justement les opérations d'insertion/suppression qui sont plus efficace lorsqu'elles sont faites au beau milieu de la collection.