2011-02-02 24 views
9

Tworzę kilka tablic bajtowych, które muszą być połączone, aby utworzyć jedną dużą tablicę bajtów - wolałbym nie używać bajtu [] w ogóle, ale nie mam tutaj wyboru. ..Łączenie listy bajtów C# []

Dodaję każdy do listy, ponieważ je tworzę, więc muszę zrobić tylko konkatenację, gdy mam wszystkie bajty [], ale moje pytanie brzmi, jaki jest najlepszy sposób robiąc to?

Kiedy mam listę z nieznaną liczbą bajtów [] i chcę połączyć je wszystkie razem.

Dzięki.

Odpowiedz

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

Powyższy kod połączy sekwencję ciągów bajtów w jedną sekwencję - i zapisze wynik w tablicy.

Choć czytelny, to nie jest maksymalnie wydajny - to nie korzystając z faktu, że już wiedzą długości wynikowej tablicy bajtów, a tym samym pozwala uniknąć dynamicznie rozszerzony .ToArray() realizację że koniecznie wymaga wielu przydziałów i array- kopie. Ponadto, SelectMany jest zaimplementowany w kategoriach iteratorów; oznacza to wiele + wiele wywołań interfejsu, które są dość powolne. Jednak w przypadku małych rozmiarów zestawów danych jest to mało prawdopodobne.

Jeśli trzeba szybszą realizację można wykonać następujące czynności:

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

lub Martinho proponuje:

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

Niektóre czasy:

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

Korzystanie krótka Metoda łączenia tych 500500 bajtów zajmuje 15 ms, używając fa Metoda st zajmuje 0,5ms na moim komputerze - YMMV, i zauważ, że dla wielu aplikacji oba są wystarczająco szybkie ;-).

Wreszcie, można zastąpić Array.CopyTo z staticArray.Copy, niskiego poziomu Buffer.BlockCopy albo MemoryStream z powrotem zdefiniowanej przez bufor - to wszystko wykonać prawie identycznie na moich testów (64 .NET 4.0).

+4

Chociaż krótki i wyraźny, należy pamiętać, że ten kod jest bardzo powolny w porównaniu do tradycyjnego rozwiązania. Jeśli jest wystarczająco szybki, świetny, ale może nie być wystarczająco szybki. –

+0

Jakie jest "tradycyjne rozwiązanie"? – amalgamate

+0

"Tradycyjne" rozwiązanie byłoby prawdopodobnie ręczne, zagnieżdżone dla pętli. To około trzy razy wolniej niż rozwiązania oparte na kopiach, ale wciąż 10 razy szybciej niż "SelectMany". –

2

Zapisz je wszystkie do listy MemoryStream zamiast do listy. następnie wywołaj MemoryStream.ToArray(). Lub gdy masz listę, najpierw podsumuj wszystkie długości tablicy bajtów, utwórz nową tablicę bajtów o całkowitej długości i skopiuj każdą tablicę po ostatniej w dużej tablicy.

0

Zamiast przechowywać każdą tablicę bajtów w List<byte[]>, można zamiast tego dodać je bezpośrednio do List<byte>, używając metody AddRange dla każdego z nich.

1

używać 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(); 

Może szybciej rozwiązaniem byłoby (nie deklarując tablica z góry):

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

Wydaje się powyżej byłoby iteracyjne każdą tablicę tylko raz.

+0

Wygląda na to, że może to zadziałać dzięki, dam mu szansę. –

+2

Należy zauważyć, że to rozwiązanie jest O (n^2) w liczbie tablic bajtowych. (Czy widzisz dlaczego? Wskazówka: operatorzy sekwencji są * leniwi *.) Możesz zrobić to lepiej. Czy potrafisz znaleźć rozwiązanie, które jest liniowe pod względem liczby tablic bajtowych? –

+0

@Eric: thanks! Nie było dla mnie oczywiste, że rozwiązaniem jest O (n^2). Co się stanie, jeśli użyję oddzielnej metody do utworzenia przeliczalnej liczby bajtów? Zaktualizowałem odpowiedź. –

4

Oto rozwiązanie oparte na numerach Andrew Bezzub i fejesjoco's answers, przydzielające wstępnie całą potrzebną pamięć. Daje to wykorzystanie pamięci Θ (N) i czas Θ (N) (N to całkowita liczba bajtów).

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;