在 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# 中追加数组的最有效方法?的主要内容,如果未能解决你的问题,请参考以下文章