¿Cuáles son específicamente sus requisitos? ¿Está tomando la unión de las matrices o está preservando varias instancias del mismo valor? ¿Desea ordenar los elementos o desea conservar el orden en las matrices iniciales? ¿Busca eficiencia en velocidad o en líneas de código?
jason
Me encanta, "mejor" depende de cuáles sean sus requisitos.
Ady
77
Si puede usar LINQ, puede usar el Concatmétodo:IEnumerable<byte> arrays = array1.Concat(array2).Concat(array3);
casperOne
1
Intenta ser más claro en tus preguntas. Esta vaga pregunta ha causado mucha confusión entre esas personas lo suficientemente buenas como para tomarse el tiempo de responderle.
Tomé el tiempo de 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:
Nueva matriz de bytes usando System.Array.Copy - 0.2187556 segundos
Nueva matriz de bytes usando System.Buffer.BlockCopy - 0.1406286 segundos
IEnumerable <byte> usando el operador de rendimiento C # - 0.0781270 segundos
IEnumerable <byte> usando 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 usando System.Array.Copy - 0.2812554 segundos
Nueva matriz de bytes usando System.Buffer.BlockCopy - 0.2500048 segundos
IEnumerable <byte> usando el operador de rendimiento C # - 0.0625012 segundos
IEnumerable <byte> usando 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 usando System.Array.Copy - 1.0781457 segundos
Nueva matriz de bytes usando System.Buffer.BlockCopy - 1.0156445 segundos
IEnumerable <byte> usando el operador de rendimiento C # - 0.0625012 segundos
IEnumerable <byte> usando 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:
Nueva matriz de bytes usando System.Array.Copy - 13.4533833 segundos
Nueva matriz de bytes usando System.Buffer.BlockCopy - 13.1096267 segundos
IEnumerable <byte> usando el operador de rendimiento C # - 0 segundos
IEnumerable <byte> utilizando Concat de LINQ <> - 0 segundos
Entonces, si necesita una nueva matriz de bytes, use
Pero, si puede usar un IEnumerable<byte>, DEFINITIVAMENTE prefiera 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<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 System.Buffer.BlockCopysolución sea más genérica como esta:
* 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 versus IEnumerable <byte>), volví a ejecutar la última prueba de sincronización (1 millón de elementos, 4000 iteraciones), agregando un ciclo que itera sobre la matriz completa con cada pasar:
Nueva matriz de bytes usando System.Array.Copy - 78.20550510 segundos
Nueva matriz de bytes usando System.Buffer.BlockCopy - 77.89261900 segundos
IEnumerable <byte> usando el operador de rendimiento C # - 551.7150161 segundos
IEnumerable <byte> usando 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 centrarse en la eficiencia de la creación puede pasar por alto la ineficiencia asociada con el uso. Felicitaciones, Jon.
Pero, ¿realmente lo está convirtiendo en una matriz al final, como lo requiere la pregunta? Si no, por supuesto, es más rápido, pero no cumple con los requisitos.
Jon Skeet el
18
Re: Matt Davis - No importa si sus "requisitos" necesitan convertir el IEnumerable en una matriz; todo lo que necesitan sus requisitos es que el resultado se use realmente de alguna manera . La razón por la que sus pruebas de rendimiento en IEnumerable son tan bajas es porque en realidad no está haciendo nada . LINQ no realiza ninguno de sus trabajos hasta que intente utilizar los resultados. Por esta razón, encuentro que su respuesta es objetivamente incorrecta y podría llevar a otros a usar LINQ cuando no deberían hacerlo si les importa el rendimiento.
csauve
12
Leí toda la respuesta, incluida su actualización, mi comentario se mantiene. Sé que me uniré tarde a la fiesta, pero la respuesta es muy engañosa y la primera mitad es evidentemente falsa .
csauve
14
¿Por qué la respuesta que contiene información falsa y engañosa es la respuesta más votada, y fue editada para invalidar por completo su declaración original después de que alguien (Jon Skeet) señaló que ni siquiera respondió la pregunta de los OP?
MrCC
3
Respuesta engañosa. Incluso la edición no responde la pregunta.
Serge Profafilecebook
154
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 LINQ de bytes: cualquier cosa yieldque haga será 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 IList<T>implementación). Sin embargo, supondré que Superdumbell sabe lo que quiere.
(EDITAR: acabo de tener otro pensamiento. Hay una gran diferencia semántica entre hacer una copia de las matrices y leerlas perezosamente. Considere qué sucede si cambia los datos en una de las matrices "fuente" después de llamar al Combine(o lo que sea ) pero antes de usar el resultado, con una evaluación diferida, ese cambio será visible. Con una copia inmediata, no lo será. Diferentes situaciones requerirán un comportamiento diferente, solo algo a tener en cuenta).
Aquí están mis métodos propuestos, que son muy similares a los contenidos en algunas de las otras respuestas, ciertamente :)
publicstaticbyte[]Combine(byte[] first,byte[] second){byte[] ret =newbyte[first.Length+ second.Length];Buffer.BlockCopy(first,0, ret,0, first.Length);Buffer.BlockCopy(second,0, ret, first.Length, second.Length);return ret;}publicstaticbyte[]Combine(byte[] first,byte[] second,byte[] third){byte[] ret =newbyte[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;}publicstaticbyte[]Combine(paramsbyte[][] arrays){byte[] ret =newbyte[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 una ineficiencia adicional.
Jon, entiendo exactamente lo que estás diciendo. Mi único punto es que a veces se hacen preguntas con una implementación particular ya en mente sin darse cuenta de que existen otras soluciones. Simplemente dar una respuesta sin ofrecer alternativas me parece un mal servicio. Pensamientos?
Matt Davis, el
1
@Matt: Sí, ofrecer alternativas es bueno, pero vale la pena explicar que son alternativas en lugar de pasarlas por la respuesta a la pregunta que se hace. (No estoy diciendo que hiciste eso, tu respuesta es muy buena)
Jon Skeet
44
(Aunque creo que su marco de actuación debe mostrar el tiempo necesario para pasar por todos los resultados en cada caso, también, para evitar dar la evaluación perezosa una ventaja injusta.)
Jon Skeet
1
Incluso sin cumplir con el requisito de "el resultado debe ser una matriz", simplemente cumplir con el requisito de "el resultado debe ser usado de alguna manera" haría que LINQ no sea óptimo. ¡Creo que el requisito de poder usar el resultado debería ser implícito!
csauve
2
@andleer: Aparte de cualquier otra cosa, Buffer.BlockCopy solo funciona con tipos primitivos.
Jon Skeet
44
Llevé 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.
Solución corta y simple, una prueba de rendimiento sería genial.
Sebastian
3
Esto es definitivamente claro, legible, no requiere bibliotecas / ayudantes externos y, en términos de tiempo de desarrollo, es bastante eficiente. Excelente cuando el rendimiento en tiempo de ejecución no es crítico.
binki
28
Si simplemente necesita una nueva matriz de bytes, utilice lo siguiente:
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)yieldreturn b;foreach(byte b in a2)yieldreturn b;foreach(byte b in a3)yieldreturn b;}
He hecho algo similar a su segunda opción para fusionar transmisiones grandes, funcionó de maravilla. :)
Greg D
2
La segunda opción es genial. +1.
R. Martinho Fernandes
11
De hecho, me encontré con algunos problemas con el uso de Concat ... (con matrices en los 10 millones, en realidad se bloqueó).
Descubrí que lo siguiente es simple, fácil y funciona lo suficientemente bien sin chocar conmigo, y funciona para CUALQUIER número de matrices (no solo tres) (Utiliza LINQ):
La clase de flujo de memoria hace este trabajo bastante bien para mí. No pude hacer que la clase de búfer se ejecute tan rápido como el flujo de memoria.
using (MemoryStream ms =newMemoryStream()){
ms.Write(BitConverter.GetBytes(22),0,4);
ms.Write(BitConverter.GetBytes(44),0,4);
ms.ToArray();}
Como mencionamos, hice una prueba en un ciclo 10,000,000 de veces, y MemoryStream salió 290% MÁS LENTO que Buffer.BlockCopy
esac
En algunos casos, puede estar iterando sobre una serie de matrices sin conocimiento previo de las longitudes de las matrices individuales. Esto funciona bien en este escenario. BlockCopy se basa en tener una matriz de destino preparada previamente
Sentinel
Como dijo @Sentinel, esta respuesta es perfecta para mí porque no conozco el tamaño de las cosas que tengo que escribir y me permite hacer las cosas de manera muy limpia. ¡También funciona bien con .NET Core 3 [ReadOnly] Span <byte>!
Agua
Si inicializa MemoryStream con el tamaño final del tamaño, no se volverá a crear y será más rápido @esac.
Lamentablemente, esto no funcionará con todos los tipos. Marshal.SizeOf () no podrá devolver un tamaño para muchos tipos (intente usar este método con matrices de cadenas y verá una excepción "Tipo 'System.String' no se puede ordenar como una estructura no administrada; no hay un tamaño significativo o se puede calcular el desplazamiento ". Podría intentar limitar el parámetro de tipo solo a tipos de referencia (agregando where T : struct), pero, al no ser un experto en las entrañas del CLR, no podría decir si podría obtener excepciones en ciertas estructuras también (por ejemplo, si contienen campos de tipo de referencia).
Daniel Scott,
2
publicstaticbyte[]Concat(paramsbyte[][] arrays){
using (var mem =newMemoryStream(arrays.Sum(a => a.Length))){foreach(vararrayin arrays){
mem.Write(array,0,array.Length);}return mem.ToArray();}}
Su respuesta podría ser mejor si hubiera publicado una pequeña explicación de lo que muestra este código.
AFract
1
sí concatena un conjunto de conjuntos de bytes en un conjunto de bytes grande (como este): [1,2,3] + [4,5] + [6,7] ==> [1,2,3,4,5 , 6,7]
Peter Ertl
1
Puede usar genéricos para combinar matrices. El siguiente código se puede ampliar fácilmente a tres matrices. De esta manera, nunca necesita duplicar el código para diferentes tipos de matrices. Algunas de las respuestas anteriores me parecen demasiado complejas.
Aquí hay una generalización de la respuesta proporcionada por @Jon Skeet. Básicamente es lo mismo, solo que es utilizable para cualquier tipo de matriz, no solo bytes:
¡PELIGRO! Estos métodos no funcionarán con ningún tipo de matriz con elementos de más de un byte (prácticamente todo lo que no sean matrices de bytes). Buffer.BlockCopy () funciona con cantidades de bytes, no con números de elementos de matriz. La razón por la que se puede usar fácilmente con una matriz de bytes es que cada elemento de la matriz es un solo byte, por lo que la longitud física de la matriz es igual al número de elementos. Para convertir los métodos de byte [] de John en métodos genéricos, deberá multiplicar todos los desplazamientos y longitudes por la longitud de bytes de un único elemento de matriz; de lo contrario, no copiará todos los datos.
Daniel Scott
2
Normalmente, para hacer que esto funcione, calcularía el tamaño de un solo elemento utilizando sizeof(...)y multiplicaría eso por el número de elementos que desea copiar, pero sizeof no se puede utilizar con un tipo genérico. Es posible, para algunos tipos, usar Marshal.SizeOf(typeof(T)), pero obtendrá errores de tiempo de ejecución con ciertos tipos (por ejemplo, cadenas). Alguien con un conocimiento más profundo del funcionamiento interno de los tipos CLR podrá señalar todas las trampas posibles aquí. Baste decir que escribir un método genérico de concatenación de matriz [usando BlockCopy] no es trivial.
Daniel Scott
2
Y finalmente, puede escribir un método genérico de concatenación de matrices como este casi exactamente de la manera que se muestra arriba (con un rendimiento ligeramente inferior) utilizando Array.Copy en su lugar. Simplemente reemplace todas las llamadas Buffer.BlockCopy con llamadas Array.Copy.
Gracias por la contribucion. Como ya hay una serie de respuestas altamente calificadas a esto de hace más de una década, sería útil ofrecer una explicación de lo que distingue su enfoque. ¿Por qué alguien debería usar esto en lugar de, por ejemplo, la respuesta aceptada?
Jeremy Caney
Me gusta usar métodos extendidos, porque hay un código claro para entender. Este código selecciona dos matrices con índice de inicio y cuenta y concat. También este método extendido. Entonces, esto es para todos los tipos de matriz listos para todos los tiempos
Mehmet ÜNLÜ
¡Eso tiene sentido para mí! ¿Te importa editar tu pregunta para incluir esa información? Creo que sería valioso para los futuros lectores tener eso por adelantado, para que puedan distinguir rápidamente su enfoque de las respuestas existentes. ¡Gracias!
Jeremy Caney
-1
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, creo :).
publicstaticbyte[]CombineMultipleByteArrays(List<byte[]> lstByteArray){
using (var ms =newMemoryStream()){
using (var doc =new iTextSharp.text.Document()){
using (var copy =newPdfSmartCopy(doc, ms)){
doc.Open();foreach(var p in lstByteArray){
using (var reader =newPdfReader(p)){
copy.AddDocument(reader);}}
doc.Close();}}return ms.ToArray();}}
Concat es la respuesta correcta, pero por alguna razón, una cosa manipulada es la que obtiene más votos. Si le gusta esa respuesta, tal vez le gustaría esta solución más general aún más:
IEnumerable<byte>Combine(paramsbyte[][] arrays){foreach(byte[] a in arrays)foreach(byte b in a)yieldreturn b;}
que te permitiría hacer cosas como:
byte[] c =Combine(newbyte[]{0,1,2},newbyte[]{3,4,5}).ToArray();
La pregunta específicamente pide la solución más eficiente . Enumerable.ToArray no va a ser muy eficiente, ya que no puede saber el tamaño de la matriz final para comenzar, mientras que las técnicas enrolladas a mano sí pueden.
Concat
método:IEnumerable<byte> arrays = array1.Concat(array2).Concat(array3);
Respuestas:
Para los tipos primitivos (incluidos los bytes), use en
System.Buffer.BlockCopy
lugar deSystem.Array.Copy
. Es mas rapido.Tomé el tiempo de 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:
System.Array.Copy
- 0.2187556 segundosSystem.Buffer.BlockCopy
- 0.1406286 segundosAumenté el tamaño de cada matriz a 100 elementos y volví a ejecutar la prueba:
System.Array.Copy
- 0.2812554 segundosSystem.Buffer.BlockCopy
- 0.2500048 segundosAumenté el tamaño de cada matriz a 1000 elementos y volví a ejecutar la prueba:
System.Array.Copy
- 1.0781457 segundosSystem.Buffer.BlockCopy
- 1.0156445 segundosFinalmente, 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:
System.Array.Copy
- 13.4533833 segundosSystem.Buffer.BlockCopy
- 13.1096267 segundosEntonces, si necesita una nueva matriz de bytes, use
Pero, si puede usar un
IEnumerable<byte>
, DEFINITIVAMENTE prefiera 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.Si tiene un número arbitrario de matrices y está utilizando .NET 3.5, puede hacer que la
System.Buffer.BlockCopy
solución sea más genérica como esta:* Nota: El bloque anterior requiere que agregue el siguiente espacio de nombres en la parte superior para que funcione.
Para el punto de Jon Skeet con respecto a la iteración de las estructuras de datos posteriores (matriz de bytes versus IEnumerable <byte>), volví a ejecutar la última prueba de sincronización (1 millón de elementos, 4000 iteraciones), agregando un ciclo que itera sobre la matriz completa con cada pasar:
System.Array.Copy
- 78.20550510 segundosSystem.Buffer.BlockCopy
- 77.89261900 segundosEl punto es que es MUY importante comprender la eficiencia tanto de la creación como del uso 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.
fuente
Muchas de las respuestas me parecen ignorar los requisitos establecidos:
Estos dos juntos descartan una secuencia LINQ de bytes: cualquier cosa
yield
que haga será 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
IList<T>
implementación). Sin embargo, supondré que Superdumbell sabe lo que quiere.(EDITAR: acabo de tener otro pensamiento. Hay una gran diferencia semántica entre hacer una copia de las matrices y leerlas perezosamente. Considere qué sucede si cambia los datos en una de las matrices "fuente" después de llamar al
Combine
(o lo que sea ) pero antes de usar el resultado, con una evaluación diferida, ese cambio será visible. Con una copia inmediata, no lo será. Diferentes situaciones requerirán un comportamiento diferente, solo algo a tener en cuenta).Aquí están mis métodos propuestos, que son muy similares a los contenidos en algunas de las otras respuestas, ciertamente :)
Por supuesto, la versión "params" requiere crear primero una matriz de las matrices de bytes, lo que introduce una ineficiencia adicional.
fuente
Llevé el ejemplo LINQ de Matt un paso más allá para la limpieza del código:
En mi caso, las matrices son pequeñas, por lo que no me preocupa el rendimiento.
fuente
Si simplemente necesita una nueva matriz de bytes, utilice lo siguiente:
Alternativamente, si solo necesita un único IEnumerable, considere usar el operador de rendimiento C # 2.0:
fuente
De hecho, me encontré con algunos problemas con el uso de Concat ... (con matrices en los 10 millones, en realidad se bloqueó).
Descubrí que lo siguiente es simple, fácil y funciona lo suficientemente bien sin chocar conmigo, y funciona para CUALQUIER número de matrices (no solo tres) (Utiliza LINQ):
fuente
La clase de flujo de memoria hace este trabajo bastante bien para mí. No pude hacer que la clase de búfer se ejecute tan rápido como el flujo de memoria.
fuente
fuente
where T : struct
), pero, al no ser un experto en las entrañas del CLR, no podría decir si podría obtener excepciones en ciertas estructuras también (por ejemplo, si contienen campos de tipo de referencia).fuente
Puede usar genéricos para combinar matrices. El siguiente código se puede ampliar fácilmente a tres matrices. De esta manera, nunca necesita duplicar el código para diferentes tipos de matrices. Algunas de las respuestas anteriores me parecen demasiado complejas.
fuente
Aquí hay una generalización de la respuesta proporcionada por @Jon Skeet. Básicamente es lo mismo, solo que es utilizable para cualquier tipo de matriz, no solo bytes:
fuente
sizeof(...)
y multiplicaría eso por el número de elementos que desea copiar, pero sizeof no se puede utilizar con un tipo genérico. Es posible, para algunos tipos, usarMarshal.SizeOf(typeof(T))
, pero obtendrá errores de tiempo de ejecución con ciertos tipos (por ejemplo, cadenas). Alguien con un conocimiento más profundo del funcionamiento interno de los tipos CLR podrá señalar todas las trampas posibles aquí. Baste decir que escribir un método genérico de concatenación de matriz [usando BlockCopy] no es trivial.fuente
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, creo :).
fuente
Concat es la respuesta correcta, pero por alguna razón, una cosa manipulada es la que obtiene más votos. Si le gusta esa respuesta, tal vez le gustaría esta solución más general aún más:
que te permitiría hacer cosas como:
fuente