2011-02-02 3 views
9

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

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

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

Спасибо.

ответ

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

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

Хотя читаемый, это не максимально эффективное - это не делает использование того факта, что вы уже знаете длину результирующего массива байт и, таким образом, можно избежать динамически расширенной .ToArray() реализации, обязательно включает в себя несколько распределений и array- копии. Кроме того, 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 мс, используя fa st метод занимает 0,5 мс на моей машине - YMMV, и обратите внимание, что для многих приложений они более чем достаточно быстры ;-).

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

+4

Хотя короткий и понятный, обратите внимание, что этот код очень медленный по сравнению с традиционным решением. Если это достаточно быстро, здорово, но это может быть недостаточно быстро. –

+0

Какое «традиционное решение»? – amalgamate

+0

«Традиционное» решение, вероятно, было бы ручным, вложенным для циклов. Это примерно в три раза медленнее, чем решения на основе блок-копии, но все же в 10 раз быстрее, чем 'SelectMany'. –

2

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

0

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

1

Использование 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; 
     } 
    } 
} 

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

+0

Похоже, это может сработать, спасибо, я отдам. –

+2

Обратите внимание, что это решение O (n^2) в количестве байтовых массивов. (Понимаете, почему? Подсказка: операторы последовательности * ленивы *.) Вы можете сделать лучше, чем это. Можете ли вы найти решение, линейное по числу байтовых массивов? –

+0

@ Эрик: спасибо! Для меня не было очевидным, что решение O (n^2). Что делать, если я использую отдельный метод для формирования перечислимых байтов? Я обновил ответ. –

4

Вот решение, основанное на Andrew Bezzub и fejesjoco's answers, предварительно выделив всю необходимую память. Это дает использование памяти Θ (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; 
Смежные вопросы