22 Replies - 1382 Views - Last Post: 09 August 2012 - 06:13 AM
#1
Generic List<> behaviour
Posted 05 August 2012 - 09:03 AM
I'm using the List<> class (which comes into the System.Collections.Generic dll) In order to implement the Dijkstra's algorythm.
I want to determine the "range" from a given point to all the nodes of my "map" (not a graph, but a 2d array, as my map is more like a chessboard and I thought this would be easier using a 2d array), each square (or node, although it really isn't) has its own cost (as if the 4 edges connecting to that node were the same, not already familiar with graphs so I don't really know if I'm explaining this right), and, for example I want to know my movement range, if I'm able to go through a total cost of 15 (not 15 squares, but the equivalent, as some of the squares my cost twice to move through). So I'm actually calculating, the optimal path, to get my maximum move range from a given point.
Anyway, this was not actually needed, my main problem is my implementation of the algorythm is not working, it does the first 2 iterations of the loop right, and then, it starts assigning costs lower than they should be, or a lot of the adjacent squares with the same value. I'm not asking for a sollution to this, as it's preety much impossible If I don't explain first how everything works in my program.
So I'm trying to find what's going on and why isn't this working, and I was wondering it might be the list I'm using to store the squares which are going to be checked next, I thought this list was "in order" so what I did was, to add (using the List.Add() method) in an order of priority, new squares to check to this list (which I called CheckList).
After a lot of testing, I've started to think that something may be wrong about the way my list is working, I'm using the Add() method to add new squares to check (at the end of the list, I presume, as it was stated in the msdn documentation) And use Remove(0) to remove the first object of the list (the one which I was checking after it was checked). What I think it could be wrong is that last step, so my question is:
When I remove an element from a list, what happens to the list, does it keep the order, moving all the needed elements up in the list, or it messes up?
for example: my list contains: ["bananas", "apples", "strawberries", "potatoes"]
if i remove "bananas", would my list be like this? ["apples", "strawberries", "potatoes"]
Thank you very much, any help would be appreciated.
Replies To: Generic List<> behaviour
#2
Re: Generic List<> behaviour
Posted 05 August 2012 - 09:23 AM
Quote
Yes.
Quote
if i remove "bananas", would my list be like this? ["apples", "strawberries", "potatoes"]
Yes.
Note though that using the Remove method (or for that matter the Add method) on a list while iterating over it will mess up your loop. No idea whether that's what you're doing, but it's something to be aware of.
Also note that calling Remove(0) on a List is an O(n) operation, so that might not be the best idea performance-wise. Using a Queue would probably be a better idea.
This post has been edited by sepp2k: 05 August 2012 - 09:19 AM
#3
Re: Generic List<> behaviour
Posted 05 August 2012 - 09:26 AM
I'll keep looking into it, and try to figure out what's going on, now that I know it's not a problem with my list.
Thank you very much.
#4
Re: Generic List<> behaviour
Posted 06 August 2012 - 08:33 AM
sepp2k, on 05 August 2012 - 05:23 PM, said:
What is a O(n) operation? Is it where it recomputes the whole List object?
#5
Re: Generic List<> behaviour
Posted 06 August 2012 - 08:47 AM
And yes, remove(0) is an O(n) operation because it needs to go through the whole list and move each element one place to the front.
#6
Re: Generic List<> behaviour
Posted 06 August 2012 - 10:48 AM
Now, I'll have to go look at Reflector to see how List<T> (and List) are implemented...
#7
Re: Generic List<> behaviour
Posted 06 August 2012 - 10:55 AM
Quote
That's sad, now I'll have double check some of the places where I was really assuming the faster insert at front performance. Lucky, most of the time, I append to the end.
My new learning for today...
Edit after: And Reflector confirms it. I found:
private T _items[];
This post has been edited by Skydiver: 06 August 2012 - 10:56 AM
#8
Re: Generic List<> behaviour
Posted 07 August 2012 - 09:00 AM
Quote
This is the perfect use case for a Queue<T>. Queues are first-in, first-out collections.
Skydiver,
I learned something new today. By the way, for all of you without Reflector, there are two free alternatives that are also pretty good: JetBrain's dotPeek and Telerik's JustDecompile.
#9
Re: Generic List<> behaviour
Posted 07 August 2012 - 10:54 AM
Skydiver, on 06 August 2012 - 10:48 AM, said:
From the documentation on List<T>: The List<T> class is the generic equivalent of the ArrayList class. It implements the IList<T> generic interface using an array whose size is dynamically increased as required.
Even Queue<T> uses an array (which I find odd as I'd think a linked list would provide better performance). Again, from the docs: The capacity of a Queue<T> is the number of elements the Queue<T> can hold. As elements are added to a Queue<T>, the capacity is automatically increased as required by reallocating the internal array.
This post has been edited by Momerath: 07 August 2012 - 10:58 AM
#10
Re: Generic List<> behaviour
Posted 07 August 2012 - 11:16 AM
Momerath, on 07 August 2012 - 07:54 PM, said:
It wouldn't. The (ammortized) asymptotics are the same, but working with linked lists is generally slower because of the cost of allocating new nodes and the loss of locality.
Edit: Note that the implementation of Queue.Dequeue (which is O(1)) is quite different from that of List.Remove (which is O(n)). Presumably it simply increases the starting index by 1 (whereas List.Remove moves every element in the array starting at the given index).
This post has been edited by sepp2k: 07 August 2012 - 11:23 AM
#11
Re: Generic List<> behaviour
Posted 07 August 2012 - 11:36 AM
sepp2k, on 07 August 2012 - 11:16 AM, said:
I'm guessing that it would depend on how you deal with dequeueing (spelling?) items. Using a pointer to the head of the queue would simplify it when using an array. Based on some timing tests I just did (results below) it does look like that is what they did. Going to have to dig up my Sedgewick book as I thought he implemented Queues as a linked list and performance was something he seemed to be concerned with.
Timing results:
Add to Queue 10,000 items: 3 Milliseconds
Add to List 10,000 items: 0 Milliseconds
Remove from Queue 10,000 items: 1 Milliseconds
Remove from List 10,000 items: 64 Milliseconds
Add to Queue 100,000 items: 3 Milliseconds
Add to List 100,000 items: 0 Milliseconds
Remove from Queue 100,000 items: 2 Milliseconds
Remove from List 100,000 items: 5,931 Milliseconds
Add to Queue 1,000,000 items: 26 Milliseconds
Add to List 1,000,000 items: 9 Milliseconds
Remove from Queue 1,000,000 items: 75 Milliseconds
Remove from List 1,000,000 items: 761,596 Milliseconds
I showed the three results mainly to highlight the performance of removing things from a list. It has a very bad Big O.
Edit: You modified your post while I was waiting for my timing results
This post has been edited by Momerath: 07 August 2012 - 11:40 AM
#12
Re: Generic List<> behaviour
Posted 07 August 2012 - 11:46 AM
https://gist.github.com/3288231
(pasted here, but it's easier to read at the gist)
It's most definitely using an array and a pointer to head.
While we're at it, here's List<T>'s implementation.
Now I have a question: this line is from Dequeue:
this._head = (this._head + 1) % this._array.Length;
Why the modulus?
#13
Re: Generic List<> behaviour
Posted 07 August 2012 - 11:49 AM
#14
Re: Generic List<> behaviour
Posted 07 August 2012 - 12:05 PM
PS: Regarding RemoveAt being O(n^2): I don't think that's the case. At least I have a hard time seeing how it could be.
This post has been edited by sepp2k: 07 August 2012 - 12:07 PM
#15
Re: Generic List<> behaviour
Posted 07 August 2012 - 12:20 PM
public void RemoveAt(int index)
{
if ((uint) index >= (uint) this._size)
ThrowHelper.ThrowArgumentOutOfRangeException();
--this._size;
if (index < this._size)
Array.Copy((Array) this._items, index + 1, (Array) this._items, index, this._size - index);
this._items[this._size] = default (T);
++this._version;
}
I don't think Array.Copy is O(n2).
|
|

New Topic/Question
Reply



MultiQuote




|