我在C#中有3个字节数组,需要组合成一个。什么是完成此任务的最有效方法?
Concat
方法:IEnumerable<byte> arrays = array1.Concat(array2).Concat(array3);
我在C#中有3个字节数组,需要组合成一个。什么是完成此任务的最有效方法?
Concat
方法:IEnumerable<byte> arrays = array1.Concat(array2).Concat(array3);
Answers:
对于基本类型(包括字节),请使用System.Buffer.BlockCopy
代替System.Array.Copy
。它更快。
我对每个建议的方法进行了计时,每个循环使用1个字节的3个数组执行了100万次。结果如下:
System.Array.Copy
-0.2187556秒System.Buffer.BlockCopy
-0.1406286秒我将每个数组的大小增加到100个元素,然后重新运行测试:
System.Array.Copy
-0.2812554秒System.Buffer.BlockCopy
-0.2500048秒我将每个数组的大小增加到1000个元素,然后重新运行测试:
System.Array.Copy
-1.0781457秒的新字节数组System.Buffer.BlockCopy
-1.0156445秒的新字节数组最后,我将每个数组的大小增加到一百万个元素,然后重新运行测试,每个循环仅执行4000次:
System.Array.Copy
-13.4533833秒System.Buffer.BlockCopy
-13.1096267秒因此,如果您需要一个新的字节数组,请使用
byte[] rv = new byte[a1.Length + a2.Length + a3.Length];
System.Buffer.BlockCopy(a1, 0, rv, 0, a1.Length);
System.Buffer.BlockCopy(a2, 0, rv, a1.Length, a2.Length);
System.Buffer.BlockCopy(a3, 0, rv, a1.Length + a2.Length, a3.Length);
但是,如果可以使用IEnumerable<byte>
,则肯定会首选LINQ的Concat <>方法。它仅比C#yield操作符慢一点,但更简洁,更优雅。
IEnumerable<byte> rv = a1.Concat(a2).Concat(a3);
如果您具有任意数量的数组,并且正在使用.NET 3.5,则可以使System.Buffer.BlockCopy
解决方案更加通用,如下所示:
private byte[] Combine(params byte[][] arrays)
{
byte[] rv = new byte[arrays.Sum(a => a.Length)];
int offset = 0;
foreach (byte[] array in arrays) {
System.Buffer.BlockCopy(array, 0, rv, offset, array.Length);
offset += array.Length;
}
return rv;
}
*注意:上面的块要求您在顶部添加以下名称空间才能起作用。
using System.Linq;
就乔恩·斯基特(Jon Skeet)关于后续数据结构的迭代(字节数组与IEnumerable <byte>)的观点而言,我重新运行了最后的时序测试(100万个元素,进行4000次迭代),并添加了一个循环,每次循环遍历整个数组通过:
System.Array.Copy
-78.20550510秒System.Buffer.BlockCopy
-77.89261900秒关键是,了解生成的数据结构的创建和使用效率非常重要。仅关注创作的效率可能会忽略与使用相关的效率低下。乔恩·库德斯
在我看来,许多答案都忽略了上述要求:
这两个一起排除了一个LINQ字节序列-任何带有yield
的都将使得不迭代整个序列就不可能获得最终大小。
如果这些不是真正的要求,那么LINQ可能是一个很好的解决方案(或IList<T>
实现)。但是,我假设Superdumbell知道他想要什么。
(编辑:我刚刚想到了另一种方法。制作数组的副本和懒惰地读取它们之间存在很大的语义差异。请考虑在调用Combine
(或其他方法之后,如果在“源”数组之一中更改数据,会发生什么情况)方法,但在使用结果之前-使用惰性评估,该更改将是可见的。使用即时复制,则不会显示。不同的情况将要求不同的行为-只是需要注意的事情。)
这是我提议的方法-与其他答案中包含的方法非常相似,当然:)
public static byte[] Combine(byte[] first, byte[] second)
{
byte[] ret = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
return ret;
}
public static byte[] Combine(byte[] first, byte[] second, byte[] third)
{
byte[] ret = new byte[first.Length + second.Length + third.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
Buffer.BlockCopy(third, 0, ret, first.Length + second.Length,
third.Length);
return ret;
}
public static byte[] Combine(params byte[][] arrays)
{
byte[] ret = new byte[arrays.Sum(x => x.Length)];
int offset = 0;
foreach (byte[] data in arrays)
{
Buffer.BlockCopy(data, 0, ret, offset, data.Length);
offset += data.Length;
}
return ret;
}
当然,“ params”版本需要首先创建一个字节数组数组,这会带来额外的效率低下。
如果只需要一个新的字节数组,则使用以下命令:
byte[] Combine(byte[] a1, byte[] a2, byte[] a3)
{
byte[] ret = new byte[a1.Length + a2.Length + a3.Length];
Array.Copy(a1, 0, ret, 0, a1.Length);
Array.Copy(a2, 0, ret, a1.Length, a2.Length);
Array.Copy(a3, 0, ret, a1.Length + a2.Length, a3.Length);
return ret;
}
另外,如果只需要一个IEnumerable,请考虑使用C#2.0 yield操作符:
IEnumerable<byte> Combine(byte[] a1, byte[] a2, byte[] a3)
{
foreach (byte b in a1)
yield return b;
foreach (byte b in a2)
yield return b;
foreach (byte b in a3)
yield return b;
}
memorystream类对我来说做得很好。我无法使缓冲区类像内存流一样快地运行。
using (MemoryStream ms = new MemoryStream())
{
ms.Write(BitConverter.GetBytes(22),0,4);
ms.Write(BitConverter.GetBytes(44),0,4);
ms.ToArray();
}
public static bool MyConcat<T>(ref T[] base_arr, ref T[] add_arr)
{
try
{
int base_size = base_arr.Length;
int size_T = System.Runtime.InteropServices.Marshal.SizeOf(base_arr[0]);
Array.Resize(ref base_arr, base_size + add_arr.Length);
Buffer.BlockCopy(add_arr, 0, base_arr, base_size * size_T, add_arr.Length * size_T);
}
catch (IndexOutOfRangeException ioor)
{
MessageBox.Show(ioor.Message);
return false;
}
return true;
}
where T : struct
),但是-不是CLR内部专家-我不能说您是否也可能在某些结构上获得异常(例如,如果它们包含引用类型字段)。
public static byte[] Concat(params byte[][] arrays) {
using (var mem = new MemoryStream(arrays.Sum(a => a.Length))) {
foreach (var array in arrays) {
mem.Write(array, 0, array.Length);
}
return mem.ToArray();
}
}
可以使用泛型来组合数组。以下代码可以轻松扩展为三个数组。这样,您无需为不同类型的数组重复代码。上述一些答案对我来说似乎过于复杂。
private static T[] CombineTwoArrays<T>(T[] a1, T[] a2)
{
T[] arrayCombined = new T[a1.Length + a2.Length];
Array.Copy(a1, 0, arrayCombined, 0, a1.Length);
Array.Copy(a2, 0, arrayCombined, a1.Length, a2.Length);
return arrayCombined;
}
这是@Jon Skeet提供的答案的概括。它基本上是相同的,只是它可用于任何类型的数组,而不仅仅是字节:
public static T[] Combine<T>(T[] first, T[] second)
{
T[] ret = new T[first.Length + second.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
return ret;
}
public static T[] Combine<T>(T[] first, T[] second, T[] third)
{
T[] ret = new T[first.Length + second.Length + third.Length];
Buffer.BlockCopy(first, 0, ret, 0, first.Length);
Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
Buffer.BlockCopy(third, 0, ret, first.Length + second.Length,
third.Length);
return ret;
}
public static T[] Combine<T>(params T[][] arrays)
{
T[] ret = new T[arrays.Sum(x => x.Length)];
int offset = 0;
foreach (T[] data in arrays)
{
Buffer.BlockCopy(data, 0, ret, offset, data.Length);
offset += data.Length;
}
return ret;
}
sizeof(...)
然后乘以要复制的元素数,但是sizeof不能与泛型类型一起使用。对于某些类型,可以使用Marshal.SizeOf(typeof(T))
,但是某些类型(例如,字符串)会出现运行时错误。那些对CLR类型的内部运作有更深入了解的人将能够在这里指出所有可能的陷阱。足以说写通用数组连接方法[使用BlockCopy]并非易事。
/// <summary>
/// Combine two Arrays with offset and count
/// </summary>
/// <param name="src1"></param>
/// <param name="offset1"></param>
/// <param name="count1"></param>
/// <param name="src2"></param>
/// <param name="offset2"></param>
/// <param name="count2"></param>
/// <returns></returns>
public static T[] Combine<T>(this T[] src1, int offset1, int count1, T[] src2, int offset2, int count2)
=> Enumerable.Range(0, count1 + count2).Select(a => (a < count1) ? src1[offset1 + a] : src2[offset2 + a - count1]).ToArray();
您只需要传递字节数组列表,此函数将为您返回字节数组(合并)。这是我认为最好的解决方案:)。
public static byte[] CombineMultipleByteArrays(List<byte[]> lstByteArray)
{
using (var ms = new MemoryStream())
{
using (var doc = new iTextSharp.text.Document())
{
using (var copy = new PdfSmartCopy(doc, ms))
{
doc.Open();
foreach (var p in lstByteArray)
{
using (var reader = new PdfReader(p))
{
copy.AddDocument(reader);
}
}
doc.Close();
}
}
return ms.ToArray();
}
}
Concat是正确的答案,但由于某种原因,手动操作获得了最多的选票。如果您喜欢这个答案,也许您会更喜欢这个更通用的解决方案:
IEnumerable<byte> Combine(params byte[][] arrays)
{
foreach (byte[] a in arrays)
foreach (byte b in a)
yield return b;
}
这会让您执行以下操作:
byte[] c = Combine(new byte[] { 0, 1, 2 }, new byte[] { 3, 4, 5 }).ToArray();