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


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

 203
Author: casperOne, 2009-01-06

12 answers

Para tipos primitivos (incluyendo bytes), use System.Buffer.BlockCopy en lugar de System.Array.Copy. Es más rápido.

Cronometré 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. Nueva matriz de bytes usando System.Array.Copy - 0.2187556 segundos
  2. Nueva matriz de bytes usando System.Buffer.BlockCopy - 0.1406286 segundos
  3. {[27] }umumerable usando el operador de rendimiento C# - 0.0781270 segundos
  4. IEnumerable usando el Concat de LINQ - 0.0781270 segundos

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

  1. Nueva matriz de bytes usando System.Array.Copy - 0.2812554 segundos
  2. Nueva matriz de bytes usando System.Buffer.BlockCopy - 0.2500048 segundos
  3. {[27] }umumerable usando el operador de rendimiento C# - 0.0625012 segundos {[27] }umumerable usando el Concat de LINQ - 0.0781265 segundos

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

  1. Nueva matriz de bytes usando System.Array.Copy - 1.0781457 segundos
  2. Nueva matriz de bytes usando System.Buffer.BlockCopy - 1.0156445 segundos
  3. {[27] }umumerable usando el operador de rendimiento C# - 0.0625012 segundos {[27] }umumerable usando 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 bucle solo 4000 veces:

  1. Nueva matriz de Bytes usando System.Array.Copy - 13.4533833 segundos
  2. Nueva matriz de bytes usando System.Buffer.BlockCopy - 13.1096267 segundos
  3. {[27] }umumerable usando el operador de rendimiento C# - 0 segundos {[27] }umumerable usando 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 se puede utilizar un IEnumerable<byte>, DEFINITIVAMENTE prefiera el método Concat de LINQ. Es solo ligeramente más lento que el operador de rendimiento de C#, pero es más conciso y más elegante.

IEnumerable<byte> rv = a1.Concat(a2).Concat(a3);

Si tiene un número arbitrario de matrices y está utilizando. NET 3.5, puede hacer que la solución System.Buffer.BlockCopy sea más genérica como esta:

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;

Al punto de Jon Skeet con respecto a la iteración de las estructuras de datos posteriores (matriz de bytes vs .Enumerable), volví a ejecutar la última prueba de sincronización (1 millón de elementos, 4000 iteraciones), agregando un bucle que itera sobre la matriz completa con cada pasada:

  1. Nueva matriz de bytes usando System.Array.Copy - 78.20550510 segundos
  2. Nueva matriz de bytes usando System.Buffer.BlockCopy - 77.89261900 segundos
  3. {[27] }umumerable usando el operador de rendimiento C # - 551.7150161 segundos {[27] }umumerable usando el Concat de LINQ - 448.1804799 segundos

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

 278
Author: Matt Davis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-03 12:37:44

Muchas de las respuestas me parecen ignorar los requisitos establecidos:

  • El resultado debe ser una matriz de bytes
  • Debe ser lo más eficiente posible

Estos dos juntos descartan una secuencia LINQ de bytes - cualquier cosa con yield va a hacer imposible 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 el IList<T> aplicación). Sin embargo, asumo que Superdumbell sabe lo que quiere.

(EDITAR: Acabo de tener otro pensamiento. Hay una gran diferencia semántica entre hacer una copia de los arrays y leerlos perezosamente. Considere lo que sucede si cambia los datos en una de las matrices "fuente" después de llamar al método Combine (o lo que sea) 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 comportamiento diferente-solo algo que hay que tener en cuenta.)

Aquí están mis métodos propuestos-que son muy similares a los contenidos en algunas de las otras respuestas, ciertamente:)

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 "params" requiere crear primero una matriz de las matrices de bytes, lo que introduce ineficiencia adicional.

 126
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-06 09:13:28

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, los arrays son pequeños, así que no me preocupa el rendimiento.

 27
Author: Nate Barbettini,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-16 19:25:19

Si simplemente necesita una nueva matriz de bytes, 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 únicoumumerable, considere usar el operador de rendimiento C # 2.0:

IEnumerable<byte> 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;
}
 26
Author: FryGuy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-06 03:03:12

En realidad me encontré con algunos problemas con el uso de Concat... (con matrices en los 10 millones, en realidad se estrelló).

Encontré que lo siguiente es simple, fácil y funciona lo suficientemente bien sin estrellarse en mí, y funciona para CUALQUIER número de matrices (no solo tres) (Usa LINQ):

public static byte[] ConcatByteArrays(params byte[][]  arrays)
{
    return arrays.SelectMany(x => x).ToArray();
}
 8
Author: 00jt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-16 13:14:58

La clase memorystream hace este trabajo bastante bien para mí. No pude hacer que la clase de buffer corriera 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();
}
 5
Author: Andrew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-14 12:49:24
    public static bool MyConcat<T>(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;
    }
 2
Author: edd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-04-13 06:11:56
    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();
        }
    }
 2
Author: Peter Ertl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-10 17:20:42

He aquí una generalización de la respuesta proporcionada por @Jon Skeet. Es básicamente lo mismo, solo que es utilizable para cualquier tipo de array, no solo bytes:

public static T[] Combine<T>(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>(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<T>(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;
}
 1
Author: o_c,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-29 09:25:58

Puede usar genéricos para combinar matrices. El siguiente código se puede expandir fácilmente a tres matrices. De esta manera nunca necesita duplicar código para diferentes tipos de matrices. Algunas de las respuestas anteriores me parecen demasiado complejas.

private static T[] CombineTwoArrays<T>(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;
    }
 0
Author: BajaPaul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-20 20:12:24

Todo lo que necesita para pasar la lista de Matrices de Bytes y esta función le devolverá la Matriz de Bytes (Combinada). Esta es la mejor solución que creo :).

public static byte[] CombineMultipleByteArrays(List<byte[]> 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();
            }
        }
 -1
Author: ARC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-27 13:03:25

Concat es la respuesta correcta, pero por alguna razón una cosa manejada está obteniendo la mayor cantidad de votos. Si te gusta esa respuesta, tal vez te gustaría esta solución más general aún más:

    IEnumerable<byte> 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();
 -5
Author: Mark Maxham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-06 05:33:11