2011-02-02 35 views
9

Estoy creando varias matrices de bytes que se deben unir para crear una matriz de bytes grande; prefiero no utilizar byte [] pero no tengo otra opción aquí. ..Concatenar una lista C# de byte []

Estoy agregando cada uno a una Lista a medida que los creo, así que solo tengo que hacer la concatenación una vez que tengo todos los bytes [], pero mi pregunta es, ¿cuál es la mejor forma de realidad? ¿haciendo esto?

Cuando tengo una lista con un número desconocido de byte [] y quiero combinarlos todos juntos.

Gracias.

Respuesta

18
listOfByteArrs.SelectMany(byteArr=>byteArr).ToArray() 

El código anterior concatenará una secuencia de secuencias de bytes en una secuencia y almacenará el resultado en una matriz. No se trata de hacer uso del hecho de que ya sabe la longitud de la matriz de bytes resultante y por lo tanto puede evitar la .ToArray() aplicación extendida de forma dinámica que necesariamente implica múltiples asignaciones y Expandido -

Aunque legible, esto no es de máxima eficiencia copias. Además, el SelectMany se implementa en términos de iteradores; esto significa mucho + muchas llamadas a la interfaz, lo que es bastante lento. Sin embargo, para tamaños pequeños de conjuntos de datos, es poco probable que esto importe.

Si necesita una aplicación más rápido se puede hacer lo siguiente:

var output = new byte[listOfByteArrs.Sum(arr=>arr.Length)]; 
int writeIdx=0; 
foreach(var byteArr in listOfByteArrs) { 
    byteArr.CopyTo(output, writeIdx); 
    writeIdx += byteArr.Length; 
} 

o como Martinho sugiere:

var output = new byte[listOfByteArrs.Sum(arr => arr.Length)]; 
using(var stream = new MemoryStream(output)) 
    foreach (var bytes in listOfByteArrs) 
     stream.Write(bytes, 0, bytes.Length); 

Algunos tiempos:

var listOfByteArrs = Enumerable.Range(1,1000) 
    .Select(i=>Enumerable.Range(0,i).Select(x=>(byte)x).ToArray()).ToList(); 

Usando el corto método para concatenar estos 500500 bytes lleva 15 ms, utilizando el fa El método st toma 0.5ms en mi máquina - YMMV, y tenga en cuenta que para muchas aplicaciones ambas son más que suficientemente rápidas ;-).

Por último, podría reemplazar Array.CopyTo con el staticArray.Copy, el bajo nivel de Buffer.BlockCopy, o una MemoryStream con un búfer preasignado espalda - todos éstos realizan más o menos idéntica en mis pruebas (x64 .NET 4.0).

+4

Aunque es corto y claro, tenga en cuenta que este código es muy lento en comparación con la solución tradicional. Si es lo suficientemente rápido, genial, pero puede que no sea lo suficientemente rápido. –

+0

¿Cuál es la "solución tradicional"? – amalgamate

+0

La solución "tradicional" probablemente sería manual, anidada para bucles. Eso es aproximadamente tres veces más lento que las soluciones basadas en bloques de copias, pero aún 10 veces más rápido que 'SelectMany'. –

-1

hmm ¿qué hay de list.addrange?

+0

razón de para el -1? – Fredou

+0

¿AddRange convierte una lista en un byte []? No. –

2

escríbalos a un MemoryStream en lugar de a una lista. luego llama a MemoryStream.ToArray(). O cuando tenga la lista, primero resuma todas las longitudes de la matriz de bytes, cree una nueva matriz de bytes con la longitud total y copie cada matriz después de la última en la matriz grande.

0

En lugar de almacenar cada conjunto de bytes en List<byte[]>, en su lugar, puede agregarlos directamente a List<byte>, utilizando el método AddRange para cada uno.

1

utilizar LINQ:

List<byte[]> list = new List<byte[]>(); 
    list.Add(new byte[] { 1, 2, 3, 4 }); 
    list.Add(new byte[] { 1, 2, 3, 4 }); 
    list.Add(new byte[] { 1, 2, 3, 4 }); 

    IEnumerable<byte> result = Enumerable.Empty<byte>(); 

    foreach (byte[] bytes in list) 
    { 
     result = result.Concat(bytes); 
    } 

    byte[] newArray = result.ToArray(); 

Tal vez la solución más rápida sería (no declarando gama adelantado):

IEnumerable<byte> bytesEnumerable = GetBytesFromList(list); 

byte[] newArray = bytesEnumerable.ToArray(); 

private static IEnumerable<T> GetBytesFromList<T>(IEnumerable<IEnumerable<T>> list) 
{ 
    foreach (IEnumerable<T> elements in list) 
    { 
     foreach (T element in elements) 
     { 
      yield return element; 
     } 
    } 
} 

Parece que por encima de iteraría cada matriz de una sola vez.

+0

Parece que podría funcionar gracias, lo intentaré. –

+2

Tenga en cuenta que esta solución es O (n^2) en el número de matrices de bytes. (¿Ves por qué? Sugerencia: los operadores de secuencia son * flojos *). Puedes hacerlo mejor que eso. ¿Puedes encontrar una solución lineal en el número de matrices de bytes? –

+0

@Eric: ¡gracias! No fue obvio para mí que la solución es O (n^2). ¿Qué pasa si uso un método diferente para formar un enumerable de bytes? Actualicé la respuesta. –

4

He aquí una solución basada en Andrew Bezzub y fejesjoco's answers, preasignando toda la memoria necesaria por adelantado. Esto produce Θ (N) uso de memoria y Θ (N) tiempo (N es la cantidad total de bytes).

byte[] result = new byte[list.Sum(a => a.Length)]; 
using(var stream = new MemoryStream(result)) 
{ 
    foreach (byte[] bytes in list) 
    { 
     stream.Write(bytes, 0, bytes.Length); 
    } 
} 
return result;