Конкатенация C # Список байтов [] - PullRequest
10 голосов
/ 02 февраля 2011

Я создаю несколько байтовых массивов, которые необходимо объединить для создания одного большого байтового массива - я бы предпочел вообще не использовать byte [], но здесь у меня нет выбора ...

Я добавляю каждый из них в список по мере их создания, поэтому мне нужно выполнить конкатенацию только тогда, когда у меня есть все байты [], но мой вопрос: каков наилучший способ сделать это на самом деле?

Когда у меня есть список с неизвестным числом байтов [], и я хочу объединить их все вместе.

Спасибо.

Ответы [ 6 ]

20 голосов
/ 02 февраля 2011
listOfByteArrs.SelectMany(byteArr=>byteArr).ToArray()

Приведенный выше код объединит последовательность последовательностей байтов в одну последовательность и сохранит результат в массиве.

Несмотря на то, что он читаем, он не максимально эффективен - он не используеттот факт, что вы уже знаете длину результирующего байтового массива и, таким образом, можете избежать динамически расширенной реализации .ToArray(), которая обязательно включает в себя несколько выделений и копий массива.Кроме того, SelectMany реализовано в терминах итераторов;это означает много + много интерфейсных вызовов, что довольно медленно.Однако для небольших наборов данных это вряд ли имеет значение.

Если вам нужна более быстрая реализация, вы можете сделать следующее:

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

иликак предлагает Мартиньо:

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

Некоторые временные значения:

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

Использование метода short для объединения этих 500500 байт занимает 15 мс, использование быстрого метода на моей машине занимает 0,5 мс - YMMVи обратите внимание, что для многих приложений и то, и другое более чем достаточно; -).

Наконец, вы можете заменить Array.CopyTo на static Array.Copy, низкоуровневый Buffer.BlockCopy или MemoryStream с предварительно выделенным задним буфером - все они работают примерно одинаково в моих тестах (x64 .NET 4.0).

4 голосов
/ 02 февраля 2011

Вот решение, основанное на ответах Эндрю Беззуба и fejesjoco , предварительно выделяя всю необходимую память заранее. Это дает Θ (N) использования памяти и Θ (N) времени (N - общее количество байтов).

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;
2 голосов
/ 02 февраля 2011

Используйте 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();

Возможно, будет более быстрое решение (без объявления массива заранее):

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

Похоже, что выше будет повторять каждый массив только один раз.

2 голосов
/ 02 февраля 2011

запишите их все в MemoryStream вместо списка.затем вызовите MemoryStream.ToArray ().Или, когда у вас есть список, сначала суммируйте все длины байтового массива, создайте новый байтовый массив общей длиной и скопируйте каждый массив после последнего в большом массиве.

0 голосов
/ 02 февраля 2011

Вместо сохранения каждого байтового массива в List<byte[]>, вы можете вместо этого добавить их непосредственно в List<byte>, используя для каждого из них метод AddRange .

0 голосов
/ 02 февраля 2011

хм, как насчет list.addrange ?

...