Объединение списка байтов в C# []



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



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



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



Спасибо.

773   6  

6 ответов:

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

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

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

Вот решение, основанное на ответах Эндрю Беззуба и фейесьоко, предварительно выделив всю необходимую память. Это дает Θ(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;

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

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

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

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

Хм, как насчет списка.addrange ?

Comments

    Ничего не найдено.