The C# generic list System.Collections.Generic.List<T>
is implemented using a grow-able array as the backing store, in a manner similar of more array list based implementations. It is clear that this provides huge benefits when performing random (indexed) access over e.g. lists that are implemented as linked list.
I am wondering however why the choice was not made to implement it as a circular array. Such an implementation would have the same O(1) performance for indexed random access and appending to the end of the list. But would provide additional benefits, such as allowing O(1) prepend oprations (i.e. inserting new element at the front of the list) and on average halving the amount of time needed for random insertions and deletions.
Summary from some answers so far
As pointed out by @Hachet, in order for a circular array implementation to have indexing performance that is similar to that of the System.Collections.Generic.List<T>
it would be required that it always grows to a capacity that is a power of 2 (so that a cheap modulus operation can be performed). This would mean that it is not possible to size it to an exact user supplied initial capacity as is currently possibly on construction of a list instance. So that is a clear trade off question.
And as shown by some quick & dirty performance tests, indexing may be around 2-5 x slower for a circular array implementation.
With indexing being an obvious priority I can imagine this would be too big of penalty to pay versus better performance on prepend operations and slightly better performance on random inserts/deletions.
This is a duplicate with some complementary answer information
This question is indeed related to Why typical Array List implementations aren't double-ended?, which I did not discover before posting my question. It was not answered in a manner that was fully satisfactory I guess:
I haven't done any benchmarks, but it just seemed to me that there would be other bottlenecks (e.g. nonlocal loads/stores) that would beat this by quite a lot. I'll probably accept this if I don't hear something more convincing though, thanks. – Mehrdad May 27 '11 at 4:18
The answers on this question provide additional information on how indexing for a circular list could be made to perform pretty well, including a code example, and some quantitative numbers numbers that make the trade-off decisions being made much clearer. As such they provide information that is complementary to that which is present in the other question. So I agree that the intent of this question was very much the same, and I agree it should therefore be considered a duplicate. It would be a shame however if the new information now accompanying this one would be lost.
Also I am still interested in potential additional reasons for the implementation choice that may not yet be present in the answers on either question.