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

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

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

【问题讨论】:

  • 这些数组样本大小相同还是不同?
  • 除了最后一个,即 9 个包含 1000 个样本的数组和最后一个包含 555 个样本的数组,所有数组的大小都相同。

标签: c# arrays memory-management


【解决方案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 是最有效的方式。

                    【讨论】:

                      猜你喜欢
                      • 2014-10-28
                      • 2013-05-30
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2021-10-08
                      • 2011-09-05
                      • 2023-03-07
                      • 2021-10-16
                      相关资源
                      最近更新 更多