我正在创建几个字节数组,需要连接在一起才能创建一个大字节数组 - 我宁愿不使用byte [],但在这里别无选择......
我在创建它们时将每个添加到List中,所以我只需要在拥有所有byte []后进行连接,但我的问题是,实际执行此操作的最佳方法是什么?
当我有一个包含未知数量的byte []的列表时,我想将它们连在一起。
感谢。
答案 0 :(得分:20)
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;
}
或Martinho建议:
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字节需要15ms,使用快速方法在我的机器上需要0.5ms - YMMV,并注意到对于许多应用程序来说两者都足够快; - )。
最后,您可以将Array.CopyTo
替换为static
Array.Copy
,低级Buffer.BlockCopy
或带有预分配后备缓冲区的MemoryStream
- 这些在我的测试中(x64 .NET 4.0),所有内容都完全相同。
答案 1 :(得分: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;
答案 2 :(得分:2)
将它们全部写入MemoryStream而不是列表。然后调用MemoryStream.ToArray()。或者当您有列表时,首先汇总所有字节数组长度,创建一个具有总长度的新字节数组,并将每个数组复制到大数组中的最后一个数组之后。
答案 3 :(得分:2)
使用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;
}
}
}
上面似乎只会迭代每个数组一次。
答案 4 :(得分:0)
不是将每个字节数组存储到List<byte[]>
,而是可以使用List<byte>
方法将它们直接添加到{{1}}。
答案 5 :(得分:-1)
嗯list.addrange怎么样?