Comment concaténer deux tableaux de façon performante ?
Ajouter des données à un tableau démontre souvent que l'on n'a pas choisi la bonne structure de données. Une liste ou une collection seraient sans doute plus judicieuses. Cependant, dans certains cas il peut s'avérer pratique et rapide d'utiliser des tableaux. On dispose de deux tableaux de même type : Tableau_1 et Tableau_2. On souhaite concaténer dans un troisième tableau Tableau_3 les éléments de Tableau_1 avec ceux de Tableau_2. On ne connait pas a priori le nombre d'éléments des tableaux; Tableau_3 est donc nécessairement un tableau dynamique. Pour des raisons de simplification, on suppose ici que les deux tableaux débutent à 1. Il est facile d'adapter les exemples suivants à des tableaux commençant à zéro. Implémentation naïve Dim i As Integer Dim Taille_t1 As Long, Taille_t2 As Long, Taille_t3 As Long Taille_t1 = UBound(Tableau_1) Taille_t2 = UBound(Tableau_2) Taille_t3 = Taille_t1 + Taille_t2 ReDim Tableau_3(1 To Taille_t3) For i = 1 To Taille_t1 Tableau_3(i) = Tableau_1(i) Next i For i = 1 To Taille_t2 Tableau_3(Taille_t1 + i) = Tableau_2(i) Next i Cette méthode est simple à comprendre et à implémenter, mais elle impose de parcourir les deux tableaux. Les performances de cette méthode ne sont pas très bonnes, ce qui peut être pénalisant si l'opération doit être effectuée sur de grands tableaux ou très fréquemment. Implémentation optimisée : utilisation de CopyMemoryDans le cas où le type d'un tableau a une longueur fixe (type numérique, booléen, utilisateur dont les champs sont de longueurs fixes) alors les données du tableau sont contiguës en mémoire et on peut utiliser cette propriété, en utilisant la procédure CopyMemory : Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _ (pDst As Any, _ pSrc As Any, _ ByVal NbByte As Long) Cette procédure copie NbByte octets partir de l'adresse source (pSrc) vers l'adresse destination (pDst). On l'utilise de la façon suivante : Dim i As Integer Dim Taille_t1 As Long, Taille_t2 As Long, Taille_t3 As Long Taille_t1 = UBound(Tableau_1) Taille_t2 = UBound(Tableau_2) Taille_t3 = Taille_t1 + Taille_t2 ReDim Tableau_3(1 To Taille_t3) CopyMemory Tableau_3(1), Tableau_1(1), Taille_t1 * Len(T1(1)) CopyMemory Tableau_3(Taille_t1 + 1), Tableau_2(1), Taille_t2 * Len(T2(1)) Le code ci-dessus est particulièrement simple. Il suffit de dimensionner dynamiquement Tableau_3 puis de copier en une seule instruction tous les éléments de Tableau_1, puis tous les éléments de tableau_2, à la suite. Exemple complet d'implémentationVoici une implémentation possible avec des tableaux de Double, sous forme d'une Sub : Sub ConcateneTableaux(ByRef t1() As Double, ByRef t2() As Double, ByRef t3() As Double) Dim Taille_t1 As Long, Taille_t2 As Long On Error GoTo T1_vide Taille_t1 = UBound(t1) On Error GoTo T2_vide Taille_t2 = UBound(t2) On Error GoTo 0 If (Taille_t1 + Taille_t2) > 0 Then ReDim t3(1 To Taille_t1 + Taille_t2) If Taille_t1 > 0 Then CopyMemory t3(1), t1(1), Taille_t1 * Len(t1(1)) If Taille_t2 > 0 Then CopyMemory t3(1 + Taille_t1), t2(1), Taille_t2 * Len(t2(1)) End If Exit Sub T1_vide: Taille_t1 = 0 Resume Next T2_vide: Taille_t2 = 0 Resume Next End Sub
Cette méthode est environ 80 fois plus rapide que la méthode naïve. Ajout des éléments de Tableau_2 dans Tableau_1, sans tableaux intermédiairesIl n'est pas toujours utile ou même souhaitable de créer un tableau intermédiaire pour le résultat de la concaténation. Il est possible si et seulement si Tableau_1 est un tableau dynamique d'ajouter à celui-ci les éléments d'un second tableau. Voici un exemple de fonction qui réallise cette opération. Function ConcateneTableaux(Tableau_1() As Double, Tableau_2() As Double) As Boolean Dim Taille_t1 As Long, Taille_t2 As Long ConcateneTableaux = False On Error GoTo T1_est_vide Taille_t1 = UBound(Tableau_1) On Error GoTo T2_est_vide Taille_t2 = UBound(Tableau_2) On Error GoTo T1_Static ReDim Preserve Tableau_1(Taille_t1 + Taille_t2) CopyMemory Tableau_1(Taille_t1 + 1), Tableau_2(1), Taille_t2 * Len(Tableau_2(1)) On Error GoTo 0 ConcateneTableaux = True End_ConcateneTableaux: Exit Function T1_Static: ConcateneTableaux = False Resume End_ConcateneTableaux T1_est_vide: Tableau_1 = Tableau_2 ConcateneTableaux = True Resume End_ConcateneTableaux T2_est_vide: ConcateneTableaux = True Resume End_ConcateneTableaux End Function
La modification essentielle tient dans le redim qui doit préserver les premiers éléments de T1. Les différents labels correspondent aux conditions et gestions d'erreurs suivantes. Pour aller plus loin Voir aussi : |