在 C# 中追加数组的最有效方法?

Posted

技术标签:

【中文标题】在 C# 中追加数组的最有效方法?【英文标题】:Most efficient way to append arrays in C#? 【发布时间】:2010-09-23 05:16:09 【问题描述】:

我以双精度数组的形式从老式 ActiveX 中提取数据。我最初不知道我将实际检索到的最终样本数量。

当我将这些数组从系统中拉出时,在 C# 中将它们连接在一起的最有效方法是什么?

【问题讨论】:

这些数组样本大小相同还是不同? 除了最后一个,即 9 个包含 1000 个样本的数组和最后一个包含 555 个样本的数组,所有数组的大小都相同。 【参考方案1】:

您不能附加到实际的数组 - 数组的大小在创建时是固定的。相反,请使用可以根据需要增长的List<T>

或者,保留一个数组列表,并仅在获取所有内容后将它们全部连接起来。

请参阅Eric Lippert's blog post on arrays,了解我无法提供的更多细节和见解:)

【讨论】:

【参考方案2】:

使用 .Net 4 标配的 linq 扩展连接数组很简单

要记住的最重要的事情是 linq 与 IEnumerable<T> 对象一起使用,因此为了将数组作为结果返回,您必须在最后使用 .ToArray() 方法

连接两个字节数组的示例:

byte[] firstArray = 2,45,79,33;
byte[] secondArray = 55,4,7,81;
byte[] result = firstArray.Concat(secondArray).ToArray();

【讨论】:

能否提一下Concat方法的包 @Deepakparamesh, Concat() 来自System.Linq【参考方案3】:

我相信,如果您有 2 个相同类型的数组想要组合成第三个数组,那么有一种非常简单的方法可以做到这一点。

代码如下:

String[] thehtmlFiles = Directory.GetFiles(basePath, "*.html");
String[] thexmlFiles = Directory.GetFiles(basePath, "*.xml");
List<String> finalList = new List<String>(theHTMLFiles.Concat<string>(thexmlFiles));
String[] finalArray = finalList.ToArray();

【讨论】:

【参考方案4】:

我推荐在这里找到的答案:How do I concatenate two arrays in C#?

例如

var z = new int[x.Length + y.Length];
x.CopyTo(z, 0);
y.CopyTo(z, x.Length);

【讨论】:

【参考方案5】:

该解决方案看起来很有趣,但可以仅在两个语句中连接数组。当您处理大字节数组时,我认为使用链表来包含每个字节是低效的。

这是一个从流中读取字节并动态扩展字节数组的代码示例:

字节[] buf = 新字节[8192]; 字节[] 结果 = 新字节[0]; 整数计数 = 0; 做 计数 = resStream.Read(buf, 0, buf.Length); 如果(计数!= 0) Array.Resize(ref result, result.Length + count); Array.Copy(buf, 0, result, result.Length - count, count); 而(计数> 0); // 还有更多数据要读取吗? resStream.Close();

【讨论】:

【参考方案6】:

使用这个我们可以添加两个数组而没有任何循环。

我相信,如果您有 2 个相同类型的数组想要组合成一个数组,那么有一种非常简单的方法可以做到这一点。

代码如下:

String[] TextFils = Directory.GetFiles(basePath, "*.txt");
String[] ExcelFils = Directory.GetFiles(basePath, "*.xls");
String[] finalArray = TextFils.Concat(ExcelFils).ToArray();

String[] Fils = Directory.GetFiles(basePath, "*.txt");
String[] ExcelFils = Directory.GetFiles(basePath, "*.xls");
Fils = Fils.Concat(ExcelFils).ToArray();

【讨论】:

【参考方案7】:

如果您可以估计最后将出现的项目数,请使用将计数作为参数的 List 构造函数的重载。您将节省一些昂贵的列表重复。否则你必须为此付出代价。

【讨论】:

【参考方案8】:

您可能不需要将最终结果连接到连续数组中。相反,请按照 Jon 的建议继续附加到列表中。最后你会得到一个jagged array(嗯,实际上几乎是矩形的)。当您需要通过索引访问元素时,请使用以下索引方案:

double x = list[i / sampleSize][i % sampleSize];

对锯齿状数组的迭代也很简单:

for (int iRow = 0; iRow < list.Length; ++iRow) 
  double[] row = list[iRow];
  for (int iCol = 0; iCol < row.Length; ++iCol) 
    double x = row[iCol];
  

这样可以节省内存分配和复制,但元素访问速度会稍慢一些。这是否会带来净性能提升取决于您的数据大小、数据访问模式和内存限制。

【讨论】:

【参考方案9】:

这是一个基于康斯坦丁所说的可用类:

class Program

    static void Main(string[] args)
    
        FastConcat<int> i = new FastConcat<int>();
        i.Add(new int[]  0, 1, 2, 3, 4 );
        Console.WriteLine(i[0]);
        i.Add(new int[]  5, 6, 7, 8, 9 );
        Console.WriteLine(i[4]);

        Console.WriteLine("Enumerator:");
        foreach (int val in i)
            Console.WriteLine(val);

        Console.ReadLine();
    


class FastConcat<T> : IEnumerable<T>

    LinkedList<T[]> _items = new LinkedList<T[]>();
    int _count;

    public int Count
    
        get
        
            return _count;
        
    

    public void Add(T[] items)
    
        if (items == null)
            return;
        if (items.Length == 0)
            return;

        _items.AddLast(items);
        _count += items.Length;
    

    private T[] GetItemIndex(int realIndex, out int offset)
    
        offset = 0; // Offset that needs to be applied to realIndex.
        int currentStart = 0; // Current index start.

        foreach (T[] items in _items)
        
            currentStart += items.Length;
            if (currentStart > realIndex)
                return items;
            offset = currentStart;
        
        return null;
    

    public T this[int index]
    
        get
        
            int offset;
            T[] i = GetItemIndex(index, out offset);
            return i[index - offset];
        
        set
        
            int offset;
            T[] i = GetItemIndex(index, out offset);
            i[index - offset] = value;
        
    

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    
        foreach (T[] items in _items)
            foreach (T item in items)
                yield return item;
    

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    
        return GetEnumerator();
    

    #endregion

【讨论】:

【参考方案10】:

Olmo 的建议非常好,但我想补充一下: 如果你不确定尺寸,最好让它大一点而不是小一点。当列表已满时,请记住它会将其大小加倍以添加更多元素。

例如:假设您需要大约 50 个元素。如果您使用 50 个元素的大小并且最终的元素数是 51,那么您将以 100 个大小的列表结束,其中包含 49 个浪费的位置。

【讨论】:

【参考方案11】:

我有同样的问题需要添加一个特定的计数而不是整个数组,我的第一个解决方案与 Hugo 建议的相同。但我的感觉是“效率低下”,因为调整了这么多大小。

然后我记得StringBuilder 是容量优化的。接下来我问自己,它是否也适用于MemoryStream。经过一些尝试,我可以说是的。

MemoryStream 以 256 字节的最小容量开始,如有必要,会增长其最后容量的两倍,例如 256、512、1024、2048、4096、8192 等等。

我的下一个问题是,与使用 MemoryStream 相比,调整数组大小和复制需要多长时间。使用 MemoryStream 比调整数组大小和复制要快得多。

因此,我想使用MemoryStream 是最有效的方式。

【讨论】:

以上是关于在 C# 中追加数组的最有效方法?的主要内容,如果未能解决你的问题,请参考以下文章

C#里最简单向文件追加文本的方法AppendAllText

C#里最简单向文件追加文本的方法AppendAllText

C# Web API:迭代数组并追加到 HTML 表

在 C# 中将大型双数组保存为文件的最有效方法

在 Swift 中,追加数组的效率如何?

有没有更有效的方法从同一个表中追加多个列?