Meilleur moyen de combiner plusieurs tableaux d’octets en C #

J’ai des tableaux de 3 octets en C # que je dois combiner en un seul. Quelle serait la méthode la plus efficace pour mener à bien cette tâche?

Pour les types primitifs (y compris les octets), utilisez System.Buffer.BlockCopy au lieu de System.Array.Copy . C’est plus rapide.

J’ai chronométré chacune des méthodes suggérées dans une boucle exécutée 1 million de fois en utilisant 3 tableaux de 10 octets chacun. Voici les résultats:

  1. Nouveau tableau d’octets utilisant System.Array.Copy – 0.2187556 secondes
  2. Nouveau tableau d’octets utilisant System.Buffer.BlockCopy – 0.1406286 secondes
  3. IEnumerable à l’aide de l’opérateur de rendement C # – 0,0781270 secondes
  4. IEnumerable utilisant le Concat de LINQ <> – 0.0781270 secondes

J’ai augmenté la taille de chaque tableau à 100 éléments et relancé le test:

  1. Nouveau tableau d’octets utilisant System.Array.Copy – 0.2812554 secondes
  2. Nouveau tableau d’octets utilisant System.Buffer.BlockCopy – 0.2500048 secondes
  3. IEnumerable en utilisant l’opérateur de rendement C # – 0.0625012 secondes
  4. IEnumerable utilisant Concat de LINQ <> – 0.0781265 secondes

J’ai augmenté la taille de chaque tableau à 1000 éléments et relancé le test:

  1. Nouveau tableau d’octets utilisant System.Array.Copy – 1.0781457 secondes
  2. Nouveau tableau d’octets utilisant System.Buffer.BlockCopy – 1.0156445 secondes
  3. IEnumerable en utilisant l’opérateur de rendement C # – 0.0625012 secondes
  4. IEnumerable utilisant Concat de LINQ <> – 0.0781265 secondes

Enfin, j’ai augmenté la taille de chaque tableau à 1 million d’éléments et relancé le test en exécutant chaque boucle seulement 4000 fois:

  1. Nouveau tableau d’octets utilisant System.Array.Copy – 13.4533833 secondes
  2. Nouveau tableau d’octets utilisant System.Buffer.BlockCopy – 13.1096267 secondes
  3. IEnumerable en utilisant l’opérateur de rendement C # – 0 seconde
  4. IEnumerable utilisant le Concat de LINQ <> – 0 secondes

Donc, si vous avez besoin d’un nouveau tableau d’octets, utilisez

 byte[] rv = new byte[a1.Length + a2.Length + a3.Length]; System.Buffer.BlockCopy(a1, 0, rv, 0, a1.Length); System.Buffer.BlockCopy(a2, 0, rv, a1.Length, a2.Length); System.Buffer.BlockCopy(a3, 0, rv, a1.Length + a2.Length, a3.Length); 

Mais, si vous pouvez utiliser un IEnumerable , préférez DÉFINITIVEMENT la méthode Concat <> de LINQ. Il n’est que légèrement plus lent que l’opérateur de rendement C #, mais il est plus concis et plus élégant.

 IEnumerable rv = a1.Concat(a2).Concat(a3); 

Si vous avez un nombre arbitraire de tableaux et que vous utilisez .NET 3.5, vous pouvez rendre la solution System.Buffer.BlockCopy plus générique comme ceci:

 private byte[] Combine(params byte[][] arrays) { byte[] rv = new byte[arrays.Sum(a => a.Length)]; int offset = 0; foreach (byte[] array in arrays) { System.Buffer.BlockCopy(array, 0, rv, offset, array.Length); offset += array.Length; } return rv; } 

* Remarque: Le bloc ci-dessus nécessite l’ajout de l’espace de noms suivant en haut de l’écran pour qu’il fonctionne.

 using System.Linq; 

Au sharepoint Jon Skeet concernant l’itération des structures de données suivantes (tableau d’octets vs IEnumerable ), j’ai relancé le dernier test temporel (1 million d’éléments, 4000 itérations), en ajoutant une boucle itérative sur tout le tableau. passer:

  1. Nouveau tableau d’octets utilisant System.Array.Copy – 78.20550510 secondes
  2. Nouveau tableau d’octets utilisant System.Buffer.BlockCopy – 77.89261900 secondes
  3. IEnumerable à l’aide de l’opérateur de rendement C # – 551,7150161 secondes
  4. IEnumerable utilisant le Concat de LINQ <> – 448.1804799 secondes

Le fait est qu’il est TRÈS important de comprendre l’efficacité de la création et de l’utilisation de la structure de données résultante. Le simple fait de se concentrer sur l’efficacité de la création peut faire oublier l’inefficacité associée à l’utilisation. Bravo, Jon.

Beaucoup de réponses me semblent ignorer les exigences énoncées:

  • Le résultat devrait être un tableau d’octets
  • Il devrait être aussi efficace que possible

Ces deux ensemble excluent une séquence d’octets LINQ – tout ce qui a un yield rend impossible l’obtention de la taille finale sans itération dans toute la séquence.

Si ce n’est pas le cas, LINQ pourrait être une solution parfaitement adaptée (ou l’implémentation IList ). Cependant, je suppose que Superdumbell sait ce qu’il veut.

(EDIT: Je viens d’avoir une autre idée. Il ya une grande différence sémantique entre faire une copie des tableaux et les lire paresseusement. Réfléchissez à ce qui se passe si vous modifiez les données dans l’un des tableaux quelle que soit la méthode), mais avant d’utiliser le résultat – avec une évaluation paresseuse, ce changement sera visible. Avec une copie immédiate, cela ne le sera pas. Différentes situations nécessiteront un comportement différent – juste quelque chose à savoir.

Voici mes méthodes proposées – qui sont très similaires à celles contenues dans certaines des autres réponses, certainement 🙂

 public static byte[] Combine(byte[] first, byte[] second) { byte[] ret = new byte[first.Length + second.Length]; Buffer.BlockCopy(first, 0, ret, 0, first.Length); Buffer.BlockCopy(second, 0, ret, first.Length, second.Length); return ret; } public static byte[] Combine(byte[] first, byte[] second, byte[] third) { byte[] ret = new byte[first.Length + second.Length + third.Length]; Buffer.BlockCopy(first, 0, ret, 0, first.Length); Buffer.BlockCopy(second, 0, ret, first.Length, second.Length); Buffer.BlockCopy(third, 0, ret, first.Length + second.Length, third.Length); return ret; } public static byte[] Combine(params byte[][] arrays) { byte[] ret = new byte[arrays.Sum(x => x.Length)]; int offset = 0; foreach (byte[] data in arrays) { Buffer.BlockCopy(data, 0, ret, offset, data.Length); offset += data.Length; } return ret; } 

Bien sûr, la version “params” nécessite de créer d’abord un tableau des tableaux d’octets, ce qui entraîne une inefficacité supplémentaire.

J’ai pris l’exemple LINQ de Matt un pas de plus pour la propreté du code:

 byte[] rv = a1.Concat(a2).Concat(a3).ToArray(); 

Dans mon cas, les tableaux sont petits, donc je ne suis pas concerné par les performances.

Si vous avez simplement besoin d’un nouveau tableau d’octets, utilisez ce qui suit:

 byte[] Combine(byte[] a1, byte[] a2, byte[] a3) { byte[] ret = new byte[a1.Length + a2.Length + a3.Length]; Array.Copy(a1, 0, ret, 0, a1.Length); Array.Copy(a2, 0, ret, a1.Length, a2.Length); Array.Copy(a3, 0, ret, a1.Length + a2.Length, a3.Length); return ret; } 

Si vous avez simplement besoin d’un seul IEnumerable, envisagez d’utiliser l’opérateur de rendement C # 2.0:

 IEnumerable Combine(byte[] a1, byte[] a2, byte[] a3) { foreach (byte b in a1) yield return b; foreach (byte b in a2) yield return b; foreach (byte b in a3) yield return b; } 

En fait, j’ai rencontré des problèmes avec l’utilisation de Concat … (avec des tableaux dans les 10 millions, il est tombé en panne).

J’ai trouvé que ce qui suit était simple, facile et fonctionne assez bien sans que je me casse dessus, et cela fonctionne pour N’IMPORTE QUEL nombre de tableaux (pas seulement trois) (il utilise LINQ):

 public static byte[] ConcatByteArrays(params byte[][] arrays) { return arrays.SelectMany(x => x).ToArray(); } 

La classe memorystream fait ce travail assez bien pour moi. Je ne pouvais pas faire fonctionner la classe de mémoire tampon aussi vite que memorystream.

 using (MemoryStream ms = new MemoryStream()) { ms.Write(BitConverter.GetBytes(22),0,4); ms.Write(BitConverter.GetBytes(44),0,4); ms.ToArray(); } 
  public static bool MyConcat(ref T[] base_arr, ref T[] add_arr) { try { int base_size = base_arr.Length; int size_T = System.Runtime.InteropServices.Marshal.SizeOf(base_arr[0]); Array.Resize(ref base_arr, base_size + add_arr.Length); Buffer.BlockCopy(add_arr, 0, base_arr, base_size * size_T, add_arr.Length * size_T); } catch (IndexOutOfRangeException ioor) { MessageBox.Show(ioor.Message); return false; } return true; } 
  public static byte[] Concat(params byte[][] arrays) { using (var mem = new MemoryStream(arrays.Sum(a => a.Length))) { foreach (var array in arrays) { mem.Write(array, 0, array.Length); } return mem.ToArray(); } } 

Voici une généralisation de la réponse fournie par @Jon Skeet. C’est fondamentalement la même chose, seulement il est utilisable pour tout type de tableau, pas seulement les octets:

 public static T[] Combine(T[] first, T[] second) { T[] ret = new T[first.Length + second.Length]; Buffer.BlockCopy(first, 0, ret, 0, first.Length); Buffer.BlockCopy(second, 0, ret, first.Length, second.Length); return ret; } public static T[] Combine(T[] first, T[] second, T[] third) { T[] ret = new T[first.Length + second.Length + third.Length]; Buffer.BlockCopy(first, 0, ret, 0, first.Length); Buffer.BlockCopy(second, 0, ret, first.Length, second.Length); Buffer.BlockCopy(third, 0, ret, first.Length + second.Length, third.Length); return ret; } public static T[] Combine(params T[][] arrays) { T[] ret = new T[arrays.Sum(x => x.Length)]; int offset = 0; foreach (T[] data in arrays) { Buffer.BlockCopy(data, 0, ret, offset, data.Length); offset += data.Length; } return ret; } 

Peut utiliser des génériques pour combiner des tableaux. Le code suivant peut facilement être étendu à trois tableaux. De cette façon, vous n’avez jamais besoin de dupliquer le code pour différents types de tableaux. Certaines des réponses ci-dessus me semblent trop complexes.

 private static T[] CombineTwoArrays(T[] a1, T[] a2) { T[] arrayCombined = new T[a1.Length + a2.Length]; Array.Copy(a1, 0, arrayCombined, 0, a1.Length); Array.Copy(a2, 0, arrayCombined, a1.Length, a2.Length); return arrayCombined; } 

Tout ce dont vous avez besoin pour passer la liste des tableaux d’octets et cette fonction vous renverra le tableau des octets (fusionné). C’est la meilleure solution je pense :).

 public static byte[] CombineMultipleByteArrays(List lstByteArray) { using (var ms = new MemoryStream()) { using (var doc = new iTextSharp.text.Document()) { using (var copy = new PdfSmartCopy(doc, ms)) { doc.Open(); foreach (var p in lstByteArray) { using (var reader = new PdfReader(p)) { copy.AddDocument(reader); } } doc.Close(); } } return ms.ToArray(); } } 

Concat est la bonne réponse, mais pour une raison quelconque, le plus grand nombre de votes est obtenu. Si vous aimez cette réponse, vous voudrez peut-être encore plus cette solution plus générale:

  IEnumerable Combine(params byte[][] arrays) { foreach (byte[] a in arrays) foreach (byte b in a) yield return b; } 

ce qui vous permettrait de faire des choses comme:

  byte[] c = Combine(new byte[] { 0, 1, 2 }, new byte[] { 3, 4, 5 }).ToArray();