For/Next Loop or GetEnumerator ?

vis781

Well-known member
Joined
Aug 30, 2005
Messages
2,016
Location
Cambridge, UK
Programming Experience
5-10
I know this is dead lazy and i should do my own testing but can anyone tell me which of the following loops would execute faster...

VB.NET:
Dim i As Integer 
For i = 0 To al.Count - 1 
Console.WriteLine(CStr(al.Item(i))) 
Next

Or..

VB.NET:
Dim en As IEnumerator = al.GetEnumerator
While en.MoveNext 
Console.WriteLine(en.Current)
End While

Where 'al' would be an arraylist. I ask because i invariably always see people using for/next loops (that includes me) and i was thinkng that the getenumerator method would most likely get overlooked most of the time. Then i asked myself the question i wonder which one is faster? I'll have a go when i get back to my Dev PC tonight, but the mean time if anyone has a go i should be curious to know the result.
 
I find For-Next is slightly faster than GetEnumerator here.

By the way, there are some other differences in your methods, too.
1) For one you use CStr conversion, the other not which implicitly use ToString method. I also find CStr is slightly faster than ToString, but this was taken into consideration for the tests and the conclusion above :)
2) The equivalent of GetEnumerator is not "For i = 0 to.." but "For Each..", and now the table turns in favour of the GetEnumerator method by the same difference. Still "For Each.." is recommended in documentation because it "hides the complexity of the enumerators".

What I don't understand is that "For Each obj As String in al" is faster than using "For Each obj As Object in al" converting with CStr.
 
I find this interesting, the GetEnumerator method is almost pointless in the results of these tests, so why did MS provide it? There would be no benefit at all of using GetEnumerator as the information is ReadOnly and the collection can't be modified using it. The slight increase in speed must be due to the fact that the For/Next is compiled in-line. I know i'm just rambling, but wonder why MS would go to the trouble of providing a method that doesn't perform. I have since tried the tests myself and the results are completely un-interesting as the differences are inconsequential. As for Cstr i doubt it's performance, in all my tests the Convert class yields faster performance i just used Cstr because it was less typing:)
 
It's the IEnumerator.Current property that is Readonly, once you got the element you can do what you want with it and it's changed (and reflected in collection), but you can't assign it back to the Current property - this behaviour is completely analogous to the For-Each.

There are listed some benefits in the GetEnumerator help topic about varied traversal, for instance restarting iteration during loop. All the same things you can do by iterating an indexer of which you access the collection directly.

I get even worse conversion times with Convert.ToString than the Object.ToString. (doing like lots of test sets of full iteration 100.000 times and calculating average processing time in ticks)
 
Ok, so it seems that For/Each uses IEnumerator behind the scenes. In my tests the getenumerator and For/Each return very similar results the inaccuracies probably down to poor timing methods. For/Next came out at about twice the speed of the other two but i suppose that would only be usful on collections support integers as indexers. However i wonder if the performance gain on using For/Next would actually be negated by the extra work that would have to be done inside a complex loop.
 
Not necessarily only integer based indexed collections..

For Each uses an enumeration, It does not calculate the size of the colelction and use numeral indexing

The reason is that some data structures like (1 or 2 way) Linked Lists can only be accessed in an iterative/enumerative fashion.
Linked lists are like a string of sausages. If you want the 4th sausage you start at the first and count down the line by calling Next() on each sausage to get the next sausage. An accessor method might be provided called GetSausageAt(index) which will start at the start, call .Next() for INDEX number of times and return what is found
To access this using a ForNext loop might be very, very slow.. in asking sequentially for the:
0th is easy,
1st starts again and requires 1 call to Next()
2nd starts again and requires 2 calls to next()
Nth starts againa nd requires N calls to Next

you end up asking for N walks of the list, each one successively longer than the last. I.e. a Linked List of length 10, using GetItemAt(index) would require 0+1+2+3+4+5+6+7+8+9 calls to Next()

An enumerator allows us to walk up and down, accessing all 10 would require just 10 calls to .Next(), one for each item and WE OURSELVES make the calls to next. We can also ask for the previous.

So why use linked lsits? They seem slow to access?
Well, they work GREAT for queues. They dont need resizing like arrays and you can add to the start or the end. A thread can shove things into the back of the queue as another thread is eating things off the front. Each thread has O(1) access to the item it wants and there are no limits to the number of items or costly growth strategies

In summary:

ForNext
-> only use this for colelctions that have direct access from index to item. Any iterative structures such as LinkedList, do not use ForNext

ForEach
-> Use when you want forward-only read access to any collection, index linked or chain-linked. I.e. this works better for iterating all types of colelction. Is forward only. May be able to use this knowledge to secure faster access to the collection

GetEnumerator
-> Use for scrolled access to a collection, but especially when you want to scroll in any direction or want more control over starting over.

Some enumerations or iterations may allow modification of the underlying colelction. You dont really have this level of control with ForEach either because ForEach deals totally with the item in the colelction, not with accessing the collection itself. You cannot alter which is to be the next item, but sophisticated enuerators may allow this

Random Access
-> use an array, or hash linked direct access type. This isnt really iteration so i wont deal with further


Any more questions, fire away
 
Personally, for any collection that supports IEnumerator, I use ForEach. The few times that I need otherwise I tend to use ForNext. An example might be when writing 2 DataTables to file, of the same length.. I'll use ForNext to access each row.. I cannot say ForEach on the first table and get the second table relevant row.

I could start 2 enumerations and call .next() on each to achieve the same effect, but because rows are not arranged in linked list fashion and are designed as random access, the access cost of using ForNext is not greater than enumeration, so i use what people ar emore happy with

If datatables held their rows using linked lists, i would use enumerators for my 2-table simulataneous access
 
An enumerator allows us to walk up and down, accessing all 10 would require just 10 calls to .Next(), one for each item and WE OURSELVES make the calls to next. We can also ask for the previous.
How can you ask for previous with an interface that only got two methods (MoveNext, Reset) and one property (Current)? ForEach is the same thing as IEnumerator interface with the exception of explicit control of the MoveNext/Reset methods.
 
You cant, sorry.. I hoped .NET would have something comparable to java's Iterator (an evolution of Enumerator) that allows two way traversal. I didnt write the post with specific .NET enumeration in mind and it was a mistake to make .NET specific references within it - I was more going off the Java featureset and made the error of assuming they would be analogous. I've had a look around and cannot find anything that Microsoft provide for more sophisticated enumeration strategies that allow bi-directional traversal

I additionally offer the following correction of the previous post:
GetEnumerator
-> Use for scrolled access to a collection, but especially when you want to scroll in any direction or want more control over starting over.

(The concept of getting the enumerator yourself rather than using a canned loop)
-> Use for scrolled access to a collection, and in cases where the enumerator supports it, scrolling in any direction and more control over starting over.
 
Back
Top