0

There are plenty of posts about performance of for and foreach loops on arrays and IEnumerables in general. Unfortunately, they deal mostly with overheads related to foreach loop, and I can't find anything clear about their performance on linked lists - or rather List<T>.


To keep things simple, I'm going to present my question as two assumptions, and ask if they are correct.

Assumption 1

When I run the following code:

List<Foo> list = new List<Foo>();

//(list is filled here)

foreach (Foo f in list)
{
    f.baz();
}

loop iterations are going to execute like this:

0: You have a pointer to node 0. Call baz() on node 0's item. Move pointer to the node after node 0.

1: You have a pointer to node 1. Call baz() on node 1's item. Move pointer to the node after node 1.

2: You have a pointer to node 2. Call baz() on node 2's item. Move pointer to the node after node 2.

...

n: You have a pointer to node n. Call baz() on node n's item. Move pointer to the node after node n.

In other words, the code above has O(n) complexity.

Assumption 2

When I run the following code:

List<Foo> list = new List<Foo>();

//(list is filled here)

for (int i = 0; i < list.Count; i++)
{
    list[i].baz();
}

or the following code:

List<Foo> list = new List<Foo>();

//(list is filled here)

int i = 0;
while (i < list.Count)
{
    list[i].baz();
    i++;
}

loop iterations are going to execute like this:

0: You have a pointer to list. Get pointer to node 0 from list. Call baz() on node 0's item.

1: You have a pointer to list. Get pointer to node 0 from list. Move pointer to the node after node 0. Call baz() on node 1's item.

2: You have a pointer to list. Get pointer to node 0 from list. Move pointer to the node after node 0. Move pointer to the node after node 1. Call baz() on node 2's item.

...

n: You have a pointer to list. Get pointer to node 0 from list. Move pointer to the node after node 0. Move pointer to the node after node 1. Move pointer to the node after node 2. [...] Move pointer to the node after node (n-2). Move pointer to the node after node (n-1). Call baz() on node n's item.

In other words, the code above has O(n2) complexity.


Are my assumptions correct?

5
  • 1
    List is not a linked list, as you could see from reading the documentation for the type. Commented Feb 23, 2017 at 19:48
  • 1
    The major assumption you've made that's incorrect is that a linked list and a List<T> are comparable. They are not. One of the significant differences is that elements in linked lists aren't directly accessible, whereas, with List<T>, elements are directly accessible. This leads to your incorrect assumption in Assumption 2. It might help to think of List<T> as in the same category as an array. Commented Feb 23, 2017 at 20:02
  • Also, because enumerators can maintain state (of where they are), even a LinkedList enumerator can deliver O(n) when used by foreach (foreach uses enumerators under the covers). Commented Feb 23, 2017 at 20:05
  • 1
    Have you written any tests to validate? Not sure what you're asking for: our opinions? Or some metrics? If metrics, show us what code you've written and where you're stuck... :-) Commented Feb 23, 2017 at 21:48
  • @hatchet The List<T> class is the generic equivalent of the ArrayList class. Gaaaaah. How come I was so ignorant for so long? Commented Feb 24, 2017 at 17:07

1 Answer 1

1

No, your assumptions are incorrect. The List<T> data type is backed by an array not a Linked List. The logic would be

0: You have a refrence to list. Get the internal array and directly jump to the 0th index. Call baz() on node 0's item.

1: You have a refrence to list. Get the internal array and directly jump to the 1st index's offset. Call baz() on node 1's item.

2: You have a refrence to list. Get the internal array and directly jump to the 2nd index's offset. Call baz() on node 2's item.

...

n: You have a refrence to list. Get the internal array and directly jump to the nth index's offset. Call baz() on node n's item.

If you where working with a LinkedList<T> your description would be correct however LinkedList<T> does not have a indexer property [i] so you would not be able to pass in a index to retrieve like your code example does.

Sign up to request clarification or add additional context in comments.

1 Comment

"LinkedList<T> does not have a indexer property [i]" -- though, a naïve programmer might use Enumerable.ElementAt() to retrieve elements by index from a linked list, which of course would be awful in exactly the way the OP is worried about.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.