【问题标题】:Performance of for/while loop vs foreach loop on List<T>List<T> 上 for/while 循环与 foreach 循环的性能
【发布时间】:2017-07-14 11:15:18
【问题描述】:

有很多关于forforeach 循环在数组和IEnumerables 上的性能的帖子。不幸的是,它们主要处理与foreach 循环相关的开销,我找不到任何关于它们在链表上的性能的明确信息——或者更确切地说是List&lt;T&gt;


为简单起见,我将提出我的问题作为两个假设,并询问它们是否正确。

假设 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&lt;T&gt; class is the generic equivalent of the ArrayList class. Gaaaaah。我怎么这么无知这么久?

标签: c# list for-loop foreach while-loop


【解决方案1】:

不,您的假设不正确。 List&lt;T&gt; 数据类型由数组而不是链接列表支持。逻辑是

0:你有一个要列出的参考。获取内部数组,直接跳转到第0个索引。称呼 baz() 在节点 0 的项目上。

1:您有一个要列出的参考。获取内部数组并直接跳转到第一个索引的偏移量。称呼 baz() 在节点 1 的项目上。

2:你有一个要列出的参考。获取内部数组并直接跳转到第二个索引的偏移量。称呼 baz() 在节点 2 的项目上。

...

n:你有一个要列出的参考。获取内部数组,直接跳转到第n个索引的偏移量。称呼 baz() 在节点 n 的项目上。

如果您使用LinkedList&lt;T&gt;,您的描述将是正确的,但是LinkedList&lt;T&gt; 没有索引器属性[i],因此您将无法像代码示例那样传递索引来检索。

【讨论】:

  • "LinkedList 没有索引器属性 [i]"——不过,天真的程序员可能会使用 Enumerable.ElementAt() 从链接的索引中检索元素列表,这当然会像 OP 担心的那样糟糕。
猜你喜欢
  • 2010-11-13
  • 1970-01-01
  • 2012-10-02
  • 1970-01-01
  • 1970-01-01
  • 2014-12-05
  • 2012-07-14
  • 1970-01-01
相关资源
最近更新 更多