当前位置:  开发笔记 > 编程语言 > 正文

在C#中组合两个或多个字节数组的最佳方法

如何解决《在C#中组合两个或多个字节数组的最佳方法》经验,为你挑选了6个好方法。

我在C#中有3个字节的数组,我需要组合成一个.什么是最有效的方法来完成这项任务?



1> Matt Davis..:

对于原始类型(包括字节),请使用System.Buffer.BlockCopy而不是System.Array.Copy.它更快.

我使用3个10字节的数组,在一个循环中执行了100万次的每个建议方法.结果如下:

    新的字节数组使用System.Array.Copy - 0.2187556秒

    新的字节数组使用System.Buffer.BlockCopy - 0.1406286秒

    IEnumerable 使用C#yield operator - 0.0781270秒

    IEnumerable 使用LINQ的Concat <> - 0.0781270秒

我将每个数组的大小增加到100个元素并重新运行测试:

    新的字节数组使用System.Array.Copy - 0.2812554秒

    新的字节数组使用System.Buffer.BlockCopy - 0.2500048秒

    IEnumerable 使用C#yield operator - 0.0625012秒

    IEnumerable 使用LINQ的Concat <> - 0.0781265秒

我将每个数组的大小增加到1000个元素并重新运行测试:

    新的字节数组使用System.Array.Copy - 1.0781457秒

    新的字节数组使用System.Buffer.BlockCopy - 1.0156445秒

    IEnumerable 使用C#yield operator - 0.0625012秒

    IEnumerable 使用LINQ的Concat <> - 0.0781265秒

最后,我将每个数组的大小增加到100万个元素并重新运行测试,每个循环执行4000次:

    新的字节数组使用System.Array.Copy - 13.4533833秒

    新的字节数组使用System.Buffer.BlockCopy - 13.1096267秒

    IEnumerable 使用C#yield运算符 - 0秒

    IEnumerable 使用LINQ的Concat <> - 0秒

因此,如果您需要一个新的字节数组,请使用

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,那么DEFINITELY更喜欢LINQ的Concat <>方法.它只比C#yield运算符略慢,但更简洁,更优雅.

IEnumerable 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 )的观点,我重新运行了最后的定时测试(100万个元素,4000次迭代),添加了一个遍历整个数组的循环通过:

    新的字节数组使用System.Array.Copy - 78.20550510秒

    新的字节数组使用System.Buffer.BlockCopy - 77.89261900秒

    IEnumerable 使用C#yield运算符 - 551.7150161秒

    IEnumerable 使用LINQ的Concat <> - 448.1804799秒

关键是,理解结果数据结构的创建和使用的效率非常重要.仅仅关注创建的效率可能会忽略与使用相关的低效率.荣誉,乔恩.


但是问题是否真的要在最后将其转换为数组?如果没有,当然它更快 - 但它没有满足要求.
Re:Matt Davis - 如果你的"要求"需要将IEnumerable转换为数组并不重要 - 你的要求所需要的只是结果实际上*用于某些时尚*.你对IEnumerable进行性能测试的原因是因为*你实际上并没有做任何事情*!在您尝试使用结果之前,LINQ不会执行任何工作.出于这个原因,我发现你的答案客观上是不正确的,如果他们关心性能,他们绝对不应该使用LINQ.
为什么包含错误和误导性信息的答案是最高投票的答案,并且在某人(Jon Skeet)指出它甚至没有回答OP问题之后被编辑为基本完全**使其原始陈述失效**?
我读了整个答案,包括你的更新,我的评论代表.我知道我迟到了加入派对,但答案非常误导,上半部分显然是假的*.
误导性答案.即便是版本也没有回答这个问题.
是不是(懒惰)函数式编程盛大?;-)
如果产生这些结果的代码无法公开审查,这些数字与虚构一样好.
@MattDavis,你也可以发布你的测试代码吗?我不能得到与你相同的结果比例.

2> Jon Skeet..:

在我看来,许多答案都忽略了规定的要求:

结果应该是一个字节数组

它应该尽可能高效

这两个一起排除了一个LINQ字节序列 - 任何东西yield都会使得无法在不迭代整个序列的情况下获得最终大小.

如果那些当然不是真正的要求,LINQ可能是一个非常好的解决方案(或IList实现).但是,我会假设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"版本需要首先创建一个字节数组的数组,这会带来额外的低效率.


+1.答案至少应满足要求.
(虽然我认为你的表现基准应该显示在每种情况下完成所有结果所花费的时间,以避免给懒惰评估带来不公平的优势.)
@andleer:除了其他任何东西,Buffer.BlockCopy只适用于基本类型.

3> Nate Barbett..:

为了代码清洁,我进一步采用了Matt的LINQ示例:

byte[] rv = a1.Concat(a2).Concat(a3).ToArray();

就我而言,阵列很小,所以我不关心性能.


简短而简单的解决方案,性能测试会很棒!
这绝对清晰,可读,不需要外部库/助手,并且在开发时间方面非常有效.当运行时性能不重要时很棒.

4> FryGuy..:

如果您只需要一个新的字节数组,请使用以下命令:

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 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;
}


第二个选择很棒.+1.

5> 00jt..:

我实际上遇到了使用Concat的一些问题...(在1000万的阵列中,它实际上已经崩溃了).

我发现以下内容简单,容易并且运行良好而不会崩溃,它适用于任意数量的数组(不仅仅是三个)(它使用LINQ):

public static byte[] ConcatByteArrays(params byte[][]  arrays)
{
    return arrays.SelectMany(x => x).ToArray();
}



6> Andrew..:

memorystream类对我来说非常好.我无法让缓冲类以与memorystream一样快的速度运行.

using (MemoryStream ms = new MemoryStream())
{
  ms.Write(BitConverter.GetBytes(22),0,4);
  ms.Write(BitConverter.GetBytes(44),0,4);
  ms.ToArray();
}


正如qwe所说,我在循环中进行了10,000,000次测试,并且MemoryStream的出现比Buffer.BlockCopy低290%.
推荐阅读
雯颜哥_135
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有