连接字节 [] 的 C# 列表
Posted
技术标签:
【中文标题】连接字节 [] 的 C# 列表【英文标题】:Concatenating a C# List of byte[] 【发布时间】:2011-06-20 01:04:05 【问题描述】:我正在创建几个字节数组,需要将它们连接在一起以创建一个大字节数组 - 我根本不想使用 byte[],但这里别无选择...
我在创建它们时将每个都添加到列表中,因此我只需在拥有所有字节 [] 后进行连接,但我的问题是,实际执行此操作的最佳方法是什么?
当我有一个包含未知数量字节 [] 的列表并且我想将它们全部连接在一起时。
谢谢。
【问题讨论】:
【参考方案1】: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)。
【讨论】:
虽然简短明了,但请注意,与传统解决方案相比,此代码非常慢。如果它足够快,那很好,但它可能不够快。 哪个是“传统解决方案”? “传统”解决方案可能是手动的,嵌套的 for 循环。这比基于块复制的解决方案慢了大约三倍,但仍然比SelectMany
快 10 倍。【参考方案2】:
这是一个基于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;
【讨论】:
【参考方案3】:将它们全部写入 MemoryStream 而不是列表。然后调用 MemoryStream.ToArray()。或者当你有列表时,首先汇总所有字节数组长度,创建一个具有总长度的新字节数组,然后将每个数组复制到大数组中的最后一个之后。
【讨论】:
【参考方案4】:使用 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;
上面似乎只会对每个数组进行一次迭代。
【讨论】:
注意这个解在字节数组的数量上是O(n^2)。 (你明白为什么吗?提示:序列运算符是惰性的。)你可以做得更好。你能找到一个字节数组数量呈线性的解吗? @Eric:谢谢!对我来说,解决方案是 O(n^2) 并不明显。如果我使用单独的方法来形成可枚举的字节怎么办?我更新了答案。 懒惰与它有什么关系?非惰性实现也是 O(N^2)。相反,你可以有一个惰性序列和 O(N) 性能。问题是所需的接口包装 - 您需要连续包装枚举器 - 您不能声明延续。这不是一个完全公平的比较,但在 Haskell 中,概念上相似的代码例如是惰性的 和 O(N)。 确实如此。 现在概括一下。 不一定是 List您可以直接将它们添加到List<byte>
,而不是将每个字节数组存储到List<byte[]>
中,对每个字节使用AddRange 方法。
【讨论】:
【参考方案6】:嗯,list.addrange 怎么样?
【讨论】:
AddRange 是否将 List 转换为 byte[]?没有。以上是关于连接字节 [] 的 C# 列表的主要内容,如果未能解决你的问题,请参考以下文章