multiplicacion - La mejor manera de combinar dos o más matrices de bytes en C#
matriz de enteros c# (12)
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?
Aquí hay una generalización de la respuesta proporcionada por @Jon Skeet. Es básicamente el mismo, solo se puede usar para cualquier tipo de matriz, 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;
}
Concat es la respuesta correcta, pero por alguna razón, algo que se maneja manualmente es obtener la mayor cantidad de votos. Si le gusta esa respuesta, quizás le gustaría aún más esta solución más general:
IEnumerable<byte> Combine(params byte[][] arrays)
{
foreach (byte[] a in arrays)
foreach (byte b in a)
yield return b;
}
Lo que te permitiría hacer cosas como:
byte[] c = Combine(new byte[] { 0, 1, 2 }, new byte[] { 3, 4, 5 }).ToArray();
De hecho, me topé con algunos problemas con el uso de Concat ... (con arreglos en los 10 millones, se estrelló).
Encontré que lo siguiente es simple, fácil y funciona lo suficientemente bien sin chocar conmigo, y funciona para CUALQUIER número de arreglos (no solo tres) (usa 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 conseguir que la clase de búfer se ejecutara tan rápido como el flujo de memoria.
using (MemoryStream ms = new MemoryStream())
{
ms.Write(BitConverter.GetBytes(22),0,4);
ms.Write(BitConverter.GetBytes(44),0,4);
ms.ToArray();
}
Llevé el ejemplo de Matt de LINQ un paso más allá para la limpieza del código:
byte[] rv = a1.Concat(a2).Concat(a3).ToArray();
En mi caso, los arreglos son pequeños, por lo que no me preocupa el rendimiento.
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 de bytes LINQ: cualquier cosa con yield
hará que sea 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 la implementación de IList<T>
). 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 qué sucede si cambia los datos en uno de los arreglos de "fuente" después de llamar al Combine
(o lo que sea) pero antes de usar el resultado: con la evaluación perezosa, ese cambio será visible. Con una copia inmediata, no lo hará. Diferentes situaciones requerirán un comportamiento diferente, algo que debe conocer.
Aquí están mis métodos propuestos, que son muy similares a los que figuran 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 matrices de bytes, lo que introduce una ineficiencia adicional.
Para los tipos primitivos (incluidos los bytes), use System.Buffer.BlockCopy
lugar de System.Array.Copy
. Es mas rapido.
Programé cada uno de los métodos sugeridos en un bucle ejecutado 1 millón de veces usando 3 arreglos de 10 bytes cada uno. Aquí están los resultados:
- Nueva matriz de bytes que utiliza
System.Array.Copy
- 0.2187556 segundos - Nueva matriz de bytes que utiliza
System.Buffer.BlockCopy
- 0.1406286 segundos - IEnumerable <byte> usando el operador de rendimiento de C # - 0.0781270 segundos
- IEnumerable <byte> utilizando Concat de LINQ <> - 0.0781270 segundos
Aumenté el tamaño de cada matriz a 100 elementos y volví a ejecutar la prueba:
- Nueva matriz de bytes que utiliza
System.Array.Copy
- 0.2812554 segundos - Nueva matriz de bytes que utiliza
System.Buffer.BlockCopy
- 0.2500048 segundos - IEnumerable <byte> usando el operador de rendimiento de C # - 0.0625012 segundos
- IEnumerable <byte> utilizando Concat de LINQ <> - 0.0781265 segundos
Aumenté el tamaño de cada matriz a 1000 elementos y volví a ejecutar la prueba:
- Nueva matriz de bytes que utiliza
System.Array.Copy
- 1.0781457 segundos - Nueva matriz de bytes que utiliza
System.Buffer.BlockCopy
- 1.0156445 segundos - IEnumerable <byte> usando el operador de rendimiento de C # - 0.0625012 segundos
- IEnumerable <byte> utilizando 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:
- Nueva matriz de bytes que utiliza
System.Array.Copy
- 13.4533833 segundos - Nueva matriz de bytes que utiliza
System.Buffer.BlockCopy
- 13.1096267 segundos - IEnumerable <byte> usando el operador de rendimiento de C # - 0 segundos
- IEnumerable <byte> usando Concat de LINQ <> - 0 segundos
Por lo tanto, 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 puedes usar un IEnumerable<byte>
, DEFINITIVAMENTE prefieres el método Concat <> de LINQ. Es solo un poco 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
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 agregues 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. IEnumerable <byte>), volví a ejecutar la última prueba de tiempo (1 millón de elementos, 4000 iteraciones), agregando un bucle que recorre la matriz completa con cada uno pasar:
- Nueva matriz de bytes que utiliza
System.Array.Copy
- 78.20550510 segundos - Nueva matriz de bytes que utiliza
System.Buffer.BlockCopy
- 77.89261900 segundos - IEnumerable <byte> utilizando el operador de rendimiento de C # - 551.7150161 segundos
- IEnumerable <byte> utilizando Concat de LINQ <> - 448.1804799 segundos
El punto es que es MUY importante comprender la eficiencia tanto de la creación como del uso de la estructura de datos resultante. Simplemente centrándose en la eficiencia de la creación puede pasar por alto la ineficiencia asociada con el uso. Felicitaciones, Jon.
Puede usar genéricos para combinar matrices. El siguiente código se puede expandir fácilmente a tres arreglos. De esta manera, nunca necesitará duplicar código para diferentes tipos de arreglos. 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;
}
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 único IEnumerable, 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;
}
Todo lo que necesita para pasar la lista de matrices de bytes y esta función le devolverá la matriz de bytes (fusionada). 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();
}
}
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;
}
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();
}
}