我需要在一个小数组中“插入”给定索引处的元素。也就是说,将具有较大索引1的所有元素移动到右侧。 .NET中最快的方法是什么?
注意:我添加了自己的答案,但我仍在寻找解释和更快的替代方案。
编辑:我确实需要一个数组,而不是List<T>
而不是链表。
更新:由于我没有得到奇怪的效果结果的解释,我已经单独提出了这个问题:Why is copying references to strings much slower than copying ints (but vice versa for Array.Copy())?
答案 0 :(得分:4)
有两种明显的方法:使用Array.Copy
并逐个复制元素:
private static void ElementByElement<T>(T[] arg, int start) {
for (int i = arg.Length - 1; i > start; i--) {
arg[i] = arg[i - 1];
}
}
private static T BuiltInCopy<T>(T[] arg, int start) {
Array.Copy(arg, start, arg, start + 1, arg.Length - start - 1);
return arg[0];
}
一方面,List<T>
插入使用Array.Copy
。另一方面,Array.Copy
是非通用的,可以执行额外工作的批次:http://msdn.microsoft.com/en-us/library/z50k9bft.aspx
我希望Array.Copy
更快。然而,这次MiniBench测试的结果让我感到惊讶。
internal class Program {
private static int smallArraySize = 32;
public static void Main(string[] args) {
BenchArrayCopy();
}
private static void BenchArrayCopy() {
var smallArrayInt = new int[smallArraySize];
for (int i = 0; i < smallArraySize; i++)
smallArrayInt[i] = i;
var smallArrayString = new string[smallArraySize];
for (int i = 0; i < smallArraySize; i++)
smallArrayString[i] = i.ToString();
var smallArrayDateTime = new DateTime[smallArraySize];
for (int i = 0; i < smallArraySize; i++)
smallArrayDateTime[i] = DateTime.Now;
var moveInt = new TestSuite<int[], int>("Move part of array right by 1: int")
.Plus(BuiltInCopy, "Array.Copy()")
.Plus(ElementByElement, "Element by element in a for loop")
.RunTests(smallArrayInt, 0);
var moveString = new TestSuite<string[], string>("Move part of array right by 1: string")
.Plus(BuiltInCopy, "Array.Copy()")
.Plus(ElementByElement, "Element by element in a for loop")
.RunTests(smallArrayString, "0");
var moveDT = new TestSuite<DateTime[], DateTime>("Move part of array right by 1: DateTime")
.Plus(BuiltInCopy, "Array.Copy()")
.Plus(ElementByElement, "Element by element in a for loop")
.RunTests(smallArrayDateTime, smallArrayDateTime[0]);
moveInt.Display(ResultColumns.All, moveInt.FindBest());
moveString.Display(ResultColumns.All, moveInt.FindBest());
moveDT.Display(ResultColumns.All, moveInt.FindBest());
}
private static T ElementByElement<T>(T[] arg) {
int start = 8;
for (int i = smallArraySize - 1; i > start; i--) {
arg[i] = arg[i - 1];
}
return arg[0];
}
private static T BuiltInCopy<T>(T[] arg) {
int start = 8;
int length = smallArraySize - start - 1;
Array.Copy(arg, start, arg, start + 1, length);
return arg[0];
}
}
以下是两次运行的结果:
f:\MyProgramming\TimSort\Benchmarks\bin\Release>Benchmarks.exe
============ Move part of array right by 1: int ============
Array.Copy() 568475865 0:31.606 1,73
Element by element in a for loop 980013061 0:31.449 1,00
============ Move part of array right by 1: string ============
Array.Copy() 478224336 0:31.618 2,06
Element by element in a for loop 220168237 0:30.926 4,38
============ Move part of array right by 1: DateTime ============
Array.Copy() 382906030 0:27.870 2,27
Element by element in a for loop 458265102 0:29.239 1,99
f:\MyProgramming\TimSort\Benchmarks\bin\Release>Benchmarks.exe
============ Move part of array right by 1: int ============
Array.Copy() 500925013 0:28.514 1,76
Element by element in a for loop 988394220 0:31.967 1,00
============ Move part of array right by 1: string ============
Array.Copy() 483178262 0:30.048 1,92
Element by element in a for loop 193092931 0:27.642 4,43
============ Move part of array right by 1: DateTime ============
Array.Copy() 450569361 0:30.807 2,11
Element by element in a for loop 568054290 0:31.385 1,71
也就是说,int
和DateTime
ElementByElement
明显更快;而string
BuiltInCopy
的速度是ElementByElement
的两倍(而ElementByElement
的速度是int
的两倍)。我希望int
和string
的结果在32位机器上非常相似,因为堆栈上对string
的引用与{{1}的大小相同除了读写堆栈内存之外,不应该涉及任何操作。
答案 1 :(得分:2)
在这种情况下,使用linked list
可能会更好。
答案 2 :(得分:1)
首先,我会质疑一个数组是否是适合此要求的数据结构选择。但是,我可以在“逐个元素”复制代码中看到可能的优化:
private static void ElementByElement2<T>(T[] arg, int start)
{
int i = arg.Length - 1;
while (i > start)
arg[i] = arg[--i];
}
我对此进行了基准测试,其速度大约是您的for-loop解决方案的两倍。
答案 3 :(得分:1)
以List<T>
为例 - 它使用Array.Copy
,建议如果您被限制使用数组,那么这可能确实是您的最佳选择。< / p>
或者,正如Indeera所说 - 使用不同的容器!根据确切的用法,一些选项
List<T>
(或使用备用容量保留您自己的阵列,但我不能认为为什么) ,但想想它倒退 - 所以“插入”变成“追加”,这更便宜(特别是如果你不需要重新分配)List<T>
更复杂的东西 - 并保持偏移量 - 即你有一个100的数组,并知道你的“零”是50,你已经使用了20;然后在“零”处插入则简单地更改偏移49处的值,将主零偏移减少到49并将虚拟长度增加到21 答案 4 :(得分:0)
如果要添加到给定索引,则应使用链接列表。另见:What are real world examples of when Linked Lists should be used?