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

69
我正在从一个老式的ActiveX中以双精度浮点数数组的形式获取数据。我最初不知道实际检索到的样本数量的最终值。
当我将它们从系统中提取出来时,在C#中将这些数组合并在一起的最有效方法是什么?

1
这些数组样本的大小是相同的还是不同的? - Constantin
2
这些数组大小都相同,除了最后一个数组。也就是说,有9个包含1000个样本的数组,最后一个数组包含555个样本。 - Huck
11个回答

82

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

或者,保留一个数组列表,仅当您获取所有内容时将它们连接起来。

请参阅Eric Lippert 的有关数组的博客文章以了解比我能提供的更详细和深入的见解 :)


33

使用 .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();

1
请问您能否提及Concat方法所在的包(package)? - Deepak paramesh
@Deepakparamesh,Concat()来自于System.Linq - Martin_W

30

我认为如果您有两个相同类型的数组,想要将它们合并为第三个数组,有一种非常简单的方法。

以下是代码:

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();

28

7
这个解决方案看起来很有趣,但是只需要两个语句就可以连接数组。当你处理大型字节数组时,使用链表来包含每个字节可能效率低下。
以下是读取流中字节并在运行时扩展字节数组的代码示例:
byte[] buf = new byte[8192];
byte[] result = new byte[0];
int count = 0;
do
{
    count = resStream.Read(buf, 0, buf.Length);
    if (count != 0)
    {
        Array.Resize(ref result, result.Length + count);
        Array.Copy(buf, 0, result, result.Length - count, count);
    }
}
while (count > 0); // 是否还有更多数据要读取?
resStream.Close();

6

使用这个方法,我们可以在不使用任何循环的情况下将两个数组相加。

我认为如果你有两个相同类型的数组想要合并成一个数组,有一种非常简单的方法可以做到。

以下是代码:

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();

5
如果您可以大致估计最终会有多少项,请使用带有计数参数的List构造函数的重载。这样可以节省一些昂贵的List复制操作。否则,您将不得不为此付出代价。

如果我记得没错,它从床罩下面开始,有4个。 - runxc1 Bret Ferrier

4

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

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

这样做可以节省内存分配和复制,但会稍微降低元素访问速度。是否能获得性能提升取决于数据大小、数据访问模式和内存限制。


2

以下是一个基于Constantin所说内容的可用类:

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
}

0

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

例如:假设您需要约50个元素。如果您使用50个元素大小,并且最终元素数量为51,则最终会得到一个100个大小的列表,其中有49个浪费的位置。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接