【发布时间】:2017-07-14 11:15:18
【问题描述】:
有很多关于for 和foreach 循环在数组和IEnumerables 上的性能的帖子。不幸的是,它们主要处理与foreach 循环相关的开销,我找不到任何关于它们在链表上的性能的明确信息——或者更确切地说是List<T>。
为简单起见,我将提出我的问题作为两个假设,并询问它们是否正确。
假设 1
当我运行以下代码时:
List<Foo> list = new List<Foo>();
//(list is filled here)
foreach (Foo f in list)
{
f.baz();
}
循环迭代将像这样执行:
0:您有一个指向节点 0 的指针。在节点 0 的项目上调用
baz()。将指针移动到节点 0 之后的节点。1:您有一个指向节点 1 的指针。在节点 1 的项目上调用
baz()。将指针移动到节点 1 之后的节点。2:您有一个指向节点 2 的指针。在节点 2 的项目上调用
baz()。将指针移动到节点 2 之后的节点。...
n:你有一个指向节点 n 的指针。在节点 n 的项目上调用
baz()。将指针移动到节点 n 之后的节点。
换句话说,上面的代码复杂度为 O(n)。
假设 2
当我运行以下代码时:
List<Foo> list = new List<Foo>();
//(list is filled here)
for (int i = 0; i < list.Count; i++)
{
list[i].baz();
}
或以下代码:
List<Foo> list = new List<Foo>();
//(list is filled here)
int i = 0;
while (i < list.Count)
{
list[i].baz();
i++;
}
循环迭代将像这样执行:
0:你有一个指向
list的指针。从list获取指向节点 0 的指针。在节点 0 的项目上调用baz()。1:您有一个指向
list的指针。从list获取指向节点0 的指针。将指针移动到节点 0 之后的节点。在节点 1 的项目上调用baz()。2: 你有一个指向
list的指针。从list获取指向节点 0 的指针。将指针移动到节点 0 之后的节点。将指针移动到节点 1 之后的节点。在节点 2 的项目上调用baz()。...
n:您有一个指向
list的指针。从list获取指向节点0 的指针。将指针移动到节点 0 之后的节点。将指针移动到节点 1 之后的节点。将指针移动到节点 2 之后的节点。[...] 将指针移动到节点之后的节点(n-2 )。将指针移动到节点 (n-1) 之后的节点。在节点 n 的项目上调用baz()。
换句话说,上面的代码复杂度为 O(n2)。
我的假设正确吗?
【问题讨论】:
-
List不是链表,您可以从阅读该类型的文档中看到。 -
您所做的不正确的主要假设是链表和 List
具有可比性。他们不是。显着的区别之一是链表中的元素不能直接访问,而对于 List ,元素是可以直接访问的。这导致您在假设 2 中的假设不正确。将 List 视为与数组属于同一类别可能会有所帮助。 -
另外,因为枚举器可以保持状态(它们所在的位置),所以即使是 LinkedList 枚举器在被 foreach 使用时也可以提供 O(n)(foreach 在幕后使用枚举器)。
-
你写过什么测试来验证吗?不知道你在问什么:我们的意见?还是一些指标?如果是指标,请向我们展示您编写的代码以及您遇到的问题... :-)
-
@hatchet
The List<T> class is the generic equivalent of the ArrayList class.Gaaaaah。我怎么这么无知这么久?
标签: c# list for-loop foreach while-loop