Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Exemples pratiques d'utilisation de la classe Vector

Compatible ActionScript 3. Cliquer pour en savoir plus sur les compatibilités.Par gnicos (Nicolas Gauville), le 23 avril 2010
Attention : Cette astuce n’est utilisable que sous Flash 10. Si vous utilisez une version antérieure, passez votre chemin.

Dans ce tutoriel, nous allons étudier l'utilisation de la classe Vector, en partant de deux exemples réalisés avec des Arrays, que nous remplacerons par des Vectors.

Nous supposons donc que vous savez déjà utiliser les Arrays. Si ce n'est pas le cas vous pouvez lire la page Comprendre les tableaux (Array).


Premier cas: Un tableau de MovieClips

Nous allons voir un exemple dans lequel l’Array est mis en défaut. Imaginons que nous souhaitons créer un tableau qui contient des clips (donc des variables de type MovieClip), on va créer une fonction qui permet l’ajout d’un clip dans le tableau comme ceci :

monArray = new Array();
 
function ajouterClip ( clip:MovieClip ) : void
{
	monArray.push ( clip )
}

Si nous tentons d’ajouter un objet qui n’est pas une instance de MovieClip, la fonction détectera que le paramètre n’est pas bon grâce au typage ; par contre, si l’on accède directement au Array, on pourrait bien envoyer n’importe quoi d’autre, comme par exemple une chaine de caractères :

monArray.push ( new String ( "Chaine de caractères" ) );

Un Array peut contenir tous les types de variables, et il n’est pas possible de les limiter. Les Vecteurs permettent de palier à ce problème.

Les Vectors présentent un autre avantage non négligeable, dont nous parlerons à la fin de ce tutoriel.

Nous allons maintenant remplacer l’Array par un Vector : le fonctionnement reste globalement le même, à quatre différences près :

  1. Un Vector peut avoir une taille fixe, contrairement aux Array ( mais nous n’étudierons pas cette utilisation ici)
  2. Un Vector ne peut avoir une chaine de caractères en index ( = entre les crochets ), il faut impérativement une variable de type int
  3. Un vecteur ne peut pas avoir d’index vide ( à l'inverse d'un Array ). Il doit y avoir une valeur dans chaque index jusqu’au dernier ( un peu comme pour les profondeurs, avec l'effondrement des profondeurs ).
  4. Enfin, un Vector est typé. On définit le type de variable qu’il reçoit, et une erreur est levée si on essaie de mettre autre chose ; c’est ce qui va nous intéresser ici.
// On déclare notre vecteur qui ne pourra contenir que des instances de «MovieClip»:
var monTableau:Vector.<MovieClip> = new Vector.<MovieClip> ( );
// (nb : on remarque que le type du vecteur est définis entre les <  > du vecteur. )
 
function ajouterClip ( clip:MovieClip ) : void
{
	monTableau.push ( clip );
}
 
monTableau.push ( new String ( "Ahhh" ) ); // << Lève une erreur à l’execution
//Cette fois, monTableau refusera l’ajout de la chaine car il n’accepte que les MovieClips.



Deuxième cas: Une classe d’enregistrement et d’accès statique à des variables

On va imaginer que l’on souhaite faire un classe qui possède deux méthodes statiques : une pour enregistrer une variable, et une pour récupérer le contenu d’une variable. Ainsi, nous pourrons accéder aux données de cette classe dans n’importe quelle classe et faciliter l’échange de données entre classes. Voici une solution possible en utilisant un Array nommé «donnees» qui stocke les variables. Vous noterez que l’Array et les deux méthodes sont statiques, la classe ne nécessite donc pas d’instanciation, c’est une classe abstraite, d’où le fait qu’il n’y ait pas de constructeur de classe.

package
{
	public class Accesseur
	{
		// On déclare l’Array statique qui stockera les variables
		private static var donnees:Array = new Array ( );
 
		// Une méthode pour ajouter une variable
 
		public static function ajouter ( nom:String, contenu:String ) : void
		{
			donnees [ nom ] = contenu;
		}
 
		// Une méthode pour lire une variable
 
		public static function lire ( nom:String ) : String
		{
			return donnees [ nom ];
		}
	}
}

Ainsi on peut enregistrer et lire des variables dans un conteneur global accessible dans toutes nos classes à la seule condition qu’on l’importe.

Maintenant, imaginons que nous voulions l’améliorer : on souhaite ajouter deux nouvelles méthodes : l’une qui nous indique le nombre de variables enregistrées, et l’autre qui permet d’enlever une variable du Array.

C’est là le problème : pour le nombre de variables, il faudrait utiliser la propriété «length» du Array, sauf que si l’on utilise des Strings entre les crochets du vecteur cette propriété n’est plus valable. Pour la suppression, on ne peut pas utiliser la méthode splice de la classe Array puisqu’on ne peut pas non plus récupérer l’index de la variable avec indexOf.

Bien sur, on peut toujours utiliser une variable de type uint que l’on augmenterait et diminuerait à chaque ajout et suppression de variable, et rendre les variables que l’on souhaite supprimer égales à null, mais nous verrons aujourd’hui une autre solution, qui peut être préférable parce qu’elle utilise les véritables propriétés des tableaux et ne nécessite pas de simulation comme on l’aurait fait avec la variable de type uint.

Ici, cela ce complique un peu car nous avons vu qu’un vecteur ne pouvait pas avoir de String comme index, chose que nous utilisions justement dans notre classe.

Pour palier à cela, nous allons utiliser un Vecteur pour le contenu des variables, et pour l’index, nous allons tricher en utilisant un autre Vecteur, également de type String, qui contiendra les noms.

Je m’explique : pour ajouter une variable, on ajoute le nom de la variable au vecteur de noms, et le contenu au vecteur de contenus. Pour la lire, il suffit de chercher la position du nom de la variable dans le vecteur de noms, et de renvoyer la valeur du vecteur de contenu à cette position. Voici comment faire :

package
{
	public class Accesseur
	{
		// On déclare les deux Vectors, pour les noms et les contenus
		private static var noms:Vector.<String> = new Vector.<String> ( );
		private static var contenus:Vector.<String> = new Vector.<String> ( );
 
		public static function ajouter ( nom:String, contenu:String ) : void
		{
			// on ajoute le nom et le contenu à la fin des vecteurs
			noms.push ( nom );
			contenus.push ( contenu );
		}
 
		public static function lire ( nom:String ) : String
		{
			// On recherche d’abord la position du contenu :
			// Pour cela, on recherche la position du nom (qui doit être la même)
 
			// On verifie que la position est différente de -1, c’est à dire que 
			// la variable recherche existe :
 
			if ( noms.indexOf ( nom ) != -1 )
				return contenus [ noms.indexOf ( nom ) ];
			else // Sinon, on renvoie undefined car la variable n’existe pas
				return undefined;
		}
 
		// Maintenant, on peut créer les deux méthodes voulues au début :
		public static function get nombreDeVariables ( ) : int
		{
			return noms.length; // (on aurais aussi pus utiliser contenus.length )
		}
 
		public static function supprimer ( nom:String ) : void
		{
			// on recherche la position de la variable
			var position:int = noms.indexOf ( nom ); 
 
			if ( position != -1 ) // On vérifie que la variable existe
			{
				contenus.splice ( position, 1 );
				noms.splice ( position, 1 );
			}
		}
	}
}

On constate que cette fois, la valeur length existe bien, et qu’elle baisse quand on supprime une variable.

Dans le cas où on voudrait permettre à notre classe d'enregistrer tous les types de variables, il suffirait d'utiliser un Vector.<*>. Notre classe deviendrait alors :

package
{
	public class Accesseur
	{
		// On déclare les deux Vectors, pour les noms et les contenus
		private static var noms:Vector.<String> = new Vector.<String> ( );
		//Cette fois, on utilise un Vector.<*>
		private static var contenus:Vector.<*> = new Vector.<*> ( );
 
		public static function ajouter ( nom:String, contenu:* ) : void
		{
			// on ajoute le nom et le contenu à la fin des vecteurs
			noms.push ( nom );
			contenus.push ( contenu );
		}
 
		public static function lire ( nom:String ) : *
		{
			// On recherche d’abord la position du contenu :
			// Pour cela, on recherche la position du nom (qui doit être la même)
 
			// On verifie que la position est différente de -1, c’est à dire que 
			// la variable recherche existe :
 
			if ( noms.indexOf ( nom ) != -1 )
				return contenus [ noms.indexOf ( nom ) ];
			else // Sinon, on renvoie undefined car la variable n’existe pas
				return undefined;
		}
 
		// Maintenant, on peut créer les deux méthodes voulues au début :
		public static function get nombreDeVariables ( ) : int
		{
			return noms.length; // (on aurais aussi pus utiliser contenus.length )
		}
 
		public static function supprimer ( nom:String ) : void
		{
			// on recherche la position de la variable
			var position:int = noms.indexOf ( nom ); 
 
			if ( position != -1 ) // On vérifie que la variable existe
			{
				contenus.splice ( position, 1 );
				noms.splice ( position, 1 );
			}
		}
	}
}



Conclusion

Nous avons vu comment remplacer des Arrays par des Vectors dans des cas où ceux-ci semblent plus adaptés. Bien sur, cela s’avère bien plus simple lorsque le Vecteur est typé, et bien plus utile encore lorsqu’on doit avoir un nombre d’index fixe.

Cependant, je ne vous ai pas encore parlé d’un des principaux avantages du Vecteur : c’est un tableau dense, qui s’avère être beaucoup plus rapide qu’un array !

Je ne mettrai pas en évidence ces différences ici, mais c'est quand même un élément à prendre en compte. La documentation de flash en parle également :

«l'accès à l'élément de tableau et son itération sont beaucoup plus rapides lorsque vous utilisez une occurrence de Vector que lorsque vous utilisez une occurrence de Array.»
Extrait de la documentation



En savoir plus

Pour en apprendre plus sur les vecteurs: