La mejor forma de combinar dos o más matrices de bytes en C #

Tengo arreglos de 3 bytes en C # que necesito combinar en uno. ¿Cuál sería el método más eficiente para completar esta tarea?

Para los tipos primitivos (incluidos los bytes), use System.Buffer.BlockCopy lugar de System.Array.Copy . Es mas rapido.

Calculé cada uno de los métodos sugeridos en un bucle ejecutado 1 millón de veces usando 3 matrices de 10 bytes cada una. Aquí están los resultados:

  1. New Byte Array usando System.Array.Copy – 0.2187556 segundos
  2. Nueva matriz de bytes utilizando System.Buffer.BlockCopy – 0.1406286 segundos
  3. IEnumerable utilizando C # yield operator – 0.0781270 segundos
  4. IEnumerable utilizando el Concat de LINQ <> – 0.0781270 segundos

Aumenté el tamaño de cada matriz a 100 elementos y volví a ejecutar la prueba:

  1. New Byte Array usando System.Array.Copy – 0.2812554 segundos
  2. Nueva matriz de bytes utilizando System.Buffer.BlockCopy – 0.2500048 segundos
  3. IEnumerable utilizando el operador de rendimiento C # – 0.0625012 segundos
  4. IEnumerable utilizando el Concat de LINQ <> – 0.0781265 segundos

Aumenté el tamaño de cada matriz a 1000 elementos y volví a ejecutar la prueba:

  1. Nueva matriz de bytes utilizando System.Array.Copy – 1.0781457 segundos
  2. Nueva matriz de bytes utilizando System.Buffer.BlockCopy – 1.0156445 segundos
  3. IEnumerable utilizando el operador de rendimiento C # – 0.0625012 segundos
  4. IEnumerable utilizando el Concat de LINQ <> – 0.0781265 segundos

Finalmente, aumenté el tamaño de cada matriz a 1 millón de elementos y volví a ejecutar la prueba, ejecutando cada ciclo solo 4000 veces:

  1. New Byte Array usando System.Array.Copy – 13.4533833 segundos
  2. Nueva matriz de bytes utilizando System.Buffer.BlockCopy – 13.1096267 segundos
  3. IEnumerable con el operador de rendimiento C # – 0 segundos
  4. IEnumerable utilizando el Concat de LINQ <> – 0 segundos

Entonces, si necesita una nueva matriz de bytes, use

 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); 

Pero, si puede usar un IEnumerable , DEFINITIVAMENTE prefiere el método Concat <> de LINQ. Es solo un poco más lento que el operador de rendimiento C #, pero es más conciso y más elegante.

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

Si tiene una cantidad arbitraria de matrices y está utilizando .NET 3.5, puede hacer que la solución System.Buffer.BlockCopy más genérica de la siguiente manera:

 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; } 

* Nota: El bloque anterior requiere que agregue el siguiente espacio de nombres en la parte superior para que funcione.

 using System.Linq; 

Para el punto de Jon Skeet con respecto a la iteración de las estructuras de datos posteriores (matriz de bytes vs. IEnumerable ), volví a ejecutar la última prueba de tiempo (1 millón de elementos, 4000 iteraciones), agregando un ciclo que itera sobre la matriz completa con cada pasar:

  1. New Byte Array usando System.Array.Copy – 78.20550510 segundos
  2. Nueva matriz de bytes utilizando System.Buffer.BlockCopy – 77.89261900 segundos
  3. IEnumerable con el operador de rendimiento C # – 551.7150161 segundos
  4. IEnumerable utilizando el Concat de LINQ <> – 448.1804799 segundos

El punto es que es MUY importante entender la eficiencia de la creación y el uso de la estructura de datos resultante. Simplemente enfocarse en la eficiencia de la creación puede pasar por alto la ineficiencia asociada con el uso. Kudos, Jon.

Muchas de las respuestas me parecen ignorar los requisitos establecidos:

  • El resultado debería ser una matriz de bytes
  • Debe ser lo más eficiente posible

Estos dos juntos descartan una secuencia de bytes LINQ: cualquier cosa con yield impedirá obtener el tamaño final sin iterar a través de toda la secuencia.

Si esos no son los requisitos reales , por supuesto, LINQ podría ser una solución perfectamente buena (o la implementación IList ). Sin embargo, asumiré que Superdumbell sabe lo que quiere.

(EDITAR: Acabo de tener otro pensamiento. Hay una gran diferencia semántica entre hacer una copia de los arreglos y leerlos perezosamente. Considere lo que sucede si cambia los datos en uno de los arreglos “fuente” después de llamar al Combine (o sea ​​cual sea el método, pero antes de usar el resultado, con una evaluación perezosa, ese cambio será visible. Con una copia inmediata, no lo hará. Diferentes situaciones requerirán un comportamiento diferente, solo algo que debe tenerse en cuenta).

Aquí están mis métodos propuestos, que son muy similares a los que figuran en algunas de las otras respuestas, sin duda 🙂

 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; } 

Por supuesto, la versión de “params” requiere crear una matriz de matrices de bytes primero, lo que introduce una ineficiencia adicional.

Tomé el ejemplo LINQ de Matt un paso más allá para la limpieza del código:

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

En mi caso, las matrices son pequeñas, por lo que no me preocupa el rendimiento.

Si simplemente necesita una nueva matriz de bytes, entonces use lo siguiente:

 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; } 

Alternativamente, si solo necesita un solo IEnumerable, considere usar el operador de rendimiento 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; } 

De hecho, me encontré con algunos problemas con el uso de Concat … (con matrices en el 10-million, en realidad se estrelló).

Encontré que lo siguiente es simple, fácil y funciona lo suficientemente bien sin colisionar conmigo, y funciona para CUALQUIER cantidad de matrices (no solo tres) (Utiliza LINQ):

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

La clase memorystream hace este trabajo bastante bien para mí. No pude hacer que la clase de buffer se ejecutara tan rápido como 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(); } } 

Aquí hay una generalización de la respuesta proporcionada por @Jon Skeet. Es básicamente lo mismo, solo se puede usar para cualquier tipo de arreglo, no solo para los bytes:

 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; } 

Puede usar generics para combinar matrices. El siguiente código se puede expandir fácilmente a tres matrices. De esta forma, nunca necesitará duplicar el código para diferentes tipos de matrices. Algunas de las respuestas anteriores me parecen demasiado complejas.

 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; } 

Todo lo que necesita para pasar la lista de matrices de bytes y esta función le devolverá la matriz de bytes (fusionados). Esta es la mejor solución, creo 🙂

 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 es la respuesta correcta, pero por alguna razón una cosa hecha a mano es obtener la mayoría de los votos. Si te gusta esa respuesta, tal vez te gustaría esta solución más general aún más:

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

que te permitiría hacer cosas como:

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