Объединение списка байтов в C# []
Я создаю несколько байтовых массивов, которые нужно соединить вместе, чтобы создать один большой байтовый массив - я бы предпочел вообще не использовать byte [], но здесь у меня нет выбора...
Я добавляю каждый из них в список, когда я их создаю, поэтому мне нужно только выполнить конкатенацию, когда у меня есть все байты [], но мой вопрос в том, как лучше всего это сделать?
Когда у меня есть список с неизвестным числом байтов [], и я хочу объединить их все вместе.
Спасибо.
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наstaticArray.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