Question 192

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
    
    ' Calcul des taillles et Redimensionnement de Tableau_3
    Taille_t1 = UBound(Tableau_1)
    Taille_t2 = UBound(Tableau_2)
    Taille_t3 = Taille_t1 + Taille_t2
    
    ReDim Tableau_3(1 To Taille_t3)
    
    ' Copie des éléments de Tableau_1
    For i = 1 To Taille_t1
        Tableau_3(i) = Tableau_1(i)
    Next i
    
    ' Ajout des éléments de Tableau_2
    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 CopyMemory

Dans 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
    
    ' Calcul des taillles et Redimensionnement de Tableau_3
    Taille_t1 = UBound(Tableau_1)
    Taille_t2 = UBound(Tableau_2)
    Taille_t3 = Taille_t1 + Taille_t2
    
    ReDim Tableau_3(1 To Taille_t3)
    
    ' Copie de tableau_1
    CopyMemory Tableau_3(1), Tableau_1(1), Taille_t1 * Len(T1(1))
    
    ' Ajout de tableau 2
    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émentation

Voici 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édiaires

Il 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   ' Si T1 n'est pas initialisé, affecter directement T2 à T1
    Taille_t1 = UBound(Tableau_1)
        
    On Error GoTo T2_est_vide   ' Si T2 est vide, T1 est inchangé
    Taille_t2 = UBound(Tableau_2)
    
    On Error GoTo T1_Static     ' si T1 est statique, erreur
    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:
    ' Impossible de redimensionner Tableau_1
    ConcateneTableaux = False
    Resume End_ConcateneTableaux
    
T1_est_vide:    ' Affectation directe de Tableau_2 dans Tableau_1
    Tableau_1 = Tableau_2
    ConcateneTableaux = True
    Resume End_ConcateneTableaux
    
T2_est_vide:    ' alors T1 = T2
    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 :

Date de publication : 06 mars 2008
Dernière modification : 08 mars 2008
Rubriques : Généralités
Mots-clés : tableau, tableaux, concaténer, concaténation, copier, dupliquer, CopyMemory, performance, copie, mémoire, dim, redim